如何为基于Async/Await扩展的iOS网络层实现请求取消功能
解决Async/Await下的网络请求取消问题
你当前的Async/Await实现之所以不支持取消,核心原因是没把外部Task的取消信号传递给底层的HTTPClientTask。Swift并发框架提供了专门的API来处理这种场景,我们可以用withTaskCancellationHandler配合CheckedContinuation的onCancel回调来实现完整的取消逻辑。
修正后的完整扩展代码
extension HTTPClient { func dispatch(_ request: URLRequest) async throws -> (data: Data, response: HTTPURLResponse) { return try await withTaskCancellationHandler { try await withCheckedThrowingContinuation { continuation in // 发起原始请求,拿到可取消的任务实例 let task = self.dispatch(request) { result in switch result { case .success(let values): continuation.resume(returning: values) case .failure(let error): continuation.resume(throwing: error) } } // 绑定取消逻辑:当当前Async Task被取消时,调用网络任务的cancel continuation.onCancel = { task.cancel() } } } onCancel: { // 这里可以留空,因为我们已经通过continuation.onCancel处理了取消 // 若需额外的取消清理逻辑,可在此添加 } } }
关键逻辑解释
withTaskCancellationHandler:这个函数会监听当前执行的Task的取消状态,当外部调用task.cancel()时,会触发我们指定的取消逻辑。continuation.onCancel:这是CheckedContinuation的专属回调,在当前Task被取消时自动触发,我们在这里调用底层HTTPClientTask的cancel()方法,直接终止实际的网络请求。- 返回值设计:我把返回值从
HTTPClient.Result改成了直接抛出错误的形式,这更符合Async/Await的惯用写法——用throws处理错误,调用方可以用do/catch直接处理,代码更简洁自然。
调用示例
当你需要发起可取消的请求时,只需要把请求包装在一个Task里,需要取消时调用task.cancel()即可:
// 创建一个可取消的请求Task let fetchTask = Task { let httpClient = URLSessionHTTPClient(session: .shared) guard let url = URL(string: "https://your-api-endpoint.com") else { throw URLError(.badURL) } let (data, response) = try await httpClient.dispatch(URLRequest(url: url)) // 处理响应数据和状态码 } // 在需要取消的时机(比如用户返回页面)调用 fetchTask.cancel()
可选:保留Result返回类型
如果你因为现有代码兼容问题,必须返回HTTPClient.Result类型,可以用下面的写法:
extension HTTPClient { func dispatch(_ request: URLRequest) async -> HTTPClient.Result { do { let responseData = try await withTaskCancellationHandler { try await withCheckedThrowingContinuation { continuation in let task = self.dispatch(request) { result in switch result { case .success(let values): continuation.resume(returning: values) case .failure(let error): continuation.resume(throwing: error) } } continuation.onCancel = { task.cancel() } } } onCancel: {} return .success(responseData) } catch { return .failure(error) } } }
不过还是更推荐使用throws的方式,这是Swift并发设计的标准模式,能让代码更易读、更符合语言习惯。
内容的提问来源于stack exchange,提问作者Harry Blue
相关产品推荐
相关产品推荐

