Swift Concurrency:如何处理执行中异步Task的完成回调更新?
用async/await实现可更新回调的异步任务
你需要处理异步任务的结果,核心问题是当任务处于执行中时调用方法,要更新完成回调。先回顾你给出的GCD实现:
func someFunc(completion: (() -> ())?) { if isLoading { if let completion { delayedCompletion = completion } return } isLoading = true delayedCompletion = completion // 模拟异步任务 DispatchQueue.main.async { [weak self] in self?.delayedCompletion?() self?.delayedCompletion = nil } }
这个GCD逻辑的核心是:如果任务已在执行,新的completion会覆盖旧的,最终只有最后设置的completion会被执行。
你写的async/await代码的问题
你提供的代码存在严重问题:当isLoading为true时,直接用新的CheckedContinuation覆盖delayedContinuation,导致之前调用someFunc()的异步任务会永久挂起——因为旧的Continuation被丢弃,永远不会被resume,这会造成内存泄漏和任务卡死。
修正后的实现(匹配GCD逻辑)
如果要和原GCD逻辑一致(仅最后一次调用的await能获取结果,之前的调用直接返回),可以这样修改:
首先定义属性:
private var isLoading = false private var delayedContinuation: CheckedContinuation<Void, Never>?
然后实现someFunc():
func someFunc() async { if isLoading { // 先resume之前的continuation(让之前的调用结束),再保存新的 let oldContinuation = delayedContinuation delayedContinuation = nil oldContinuation?.resume() return await withCheckedContinuation { [weak self] continuation in self?.delayedContinuation = continuation } } isLoading = true await withCheckedContinuation { [weak self] continuation in self?.delayedContinuation = continuation // 替换为实际的异步任务逻辑 Task { @MainActor [weak self] in // 任务完成后恢复当前continuation self?.delayedContinuation?.resume() self?.delayedContinuation = nil self?.isLoading = false } } }
另一种实现:让所有调用都收到结果
如果希望所有调用someFunc()的地方都能在任务完成后得到通知(而不是只保留最后一个),可以用数组存储所有Continuation:
private var isLoading = false private var continuations = [CheckedContinuation<Void, Never>]() func someFunc() async { if isLoading { await withCheckedContinuation { continuation in continuations.append(continuation) } return } isLoading = true await withCheckedContinuation { continuation in continuations.append(continuation) // 替换为实际的异步任务逻辑 Task { @MainActor [weak self] in // 任务完成后恢复所有continuation self?.continuations.forEach { $0.resume() } self?.continuations.removeAll() self?.isLoading = false } } }
关键注意点
- 必须确保所有
CheckedContinuation都被resume,否则会导致异步任务永久挂起,引发内存泄漏。 - 使用
[weak self]避免循环引用,尤其是在闭包和Task中。
内容的提问来源于stack exchange,提问作者Gargo
相关产品推荐
相关产品推荐

