Swift并发:如何优雅实现同步返回取消Token+异步返回结果?
Swift并发实现带取消Token的图片加载函数
当然可以实现这种既能即时返回取消Token、又能异步返回图片的async/await函数,而且这种实现在实际开发中特别实用,比如列表滚动时要取消不可见单元格的图片请求,就刚好能用。
核心实现思路
把原来的闭包回调逻辑转成Swift并发风格,用withCheckedThrowingContinuation把URLSession的回调式任务适配成async/await的形式,同时生成UUID作为取消Token,把任务和Token绑定存储,再提供独立的取消方法就行。
完整代码实现
enum ImageLoadingError: Error { case loadingError } class ImageLoader { private var loadedImages: [URL: UIImage] = [:] private var requests: [UUID: URLSessionDataTask] = [:] // 用串行队列保证线程安全,避免多线程读写冲突 private let syncQueue = DispatchQueue(label: "com.ImageLoader.syncQueue") func loadImage(url: URL) async throws -> (token: UUID?, image: UIImage) { // 先查本地缓存,命中就直接返回,Token设为nil(因为没启动异步任务,不需要取消) if let cachedImage = syncQueue.sync { loadedImages[url] } { return (nil, cachedImage) } let cancelToken = UUID() return try await withCheckedThrowingContinuation { continuation in syncQueue.async { let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let self = self else { return } // 任务结束后,把存储的Token和任务清理掉 self.syncQueue.async { self.requests.removeValue(forKey: cancelToken) } // 处理任务被取消的情况 if let error = error as? NSError, error.code == NSURLErrorCancelled { continuation.resume(throwing: error) return } // 图片加载成功的处理 if let data, let image = UIImage(data: data) { self.syncQueue.async { self.loadedImages[url] = image } // 回到主线程返回结果(UI相关操作通常要在主线程) DispatchQueue.main.async { continuation.resume(returning: (cancelToken, image)) } return } // 加载失败,抛出错误 continuation.resume(throwing: ImageLoadingError.loadingError) } // 把任务和对应的Token存起来,方便后续取消 self.syncQueue.async { self.requests[cancelToken] = task } task.resume() } } } func cancelRequest(with token: UUID) { syncQueue.async { self.requests[token]?.cancel() self.requests.removeValue(forKey: token) print("ImageLoader: cancelling request") } } }
关键细节说明
- 用串行队列
syncQueue保护loadedImages和requests的读写,避免多线程同时操作导致的崩溃或数据错误 - 缓存命中时返回
(nil, 图片),因为没启动异步任务,自然不需要取消Token - 通过
withCheckedThrowingContinuation把老的回调逻辑转换成async/await风格,调用方可以用try await轻松获取结果,代码更简洁 - 取消方法通过Token精准找到对应的任务,调用
cancel()并清理存储,不会影响其他任务
可行性与实用性
- 可行:Swift并发模型支持通过元组同时返回即时生成的Token和异步得到的图片结果,完全满足需求
- 实用:在需要手动触发取消的场景下,这种方式比依赖
Task自带的取消机制更灵活——你可以通过Token精准控制单个任务的取消,不用去管理Task实例,尤其适合批量任务管理(比如列表图片加载、多资源并行加载的场景)
内容的提问来源于stack exchange,提问作者Matjan
相关产品推荐
相关产品推荐

