You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为基于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处理了取消
            // 若需额外的取消清理逻辑,可在此添加
        }
    }
}

关键逻辑解释

  1. withTaskCancellationHandler:这个函数会监听当前执行的Task的取消状态,当外部调用task.cancel()时,会触发我们指定的取消逻辑。
  2. continuation.onCancel:这是CheckedContinuation的专属回调,在当前Task被取消时自动触发,我们在这里调用底层HTTPClientTask的cancel()方法,直接终止实际的网络请求。
  3. 返回值设计:我把返回值从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 17:03:13