如何判断最后一次异步服务器调用结束?循环中异步调用处理方案
这个问题我太熟了——异步任务和循环结合的时候,确实很容易搞不清什么时候最后一个任务完成,尤其是循环跑的比服务器响应快得多的时候。我给你几个实用的方案,从基础到现代写法都有:
方案一:手动计数器(基础但直观)
你可以在外部维护一个线程安全的计数器,每次异步调用完成就递增计数器,当计数器等于总任务数时,就说明最后一次调用也结束了。
var completedTasks = 0 let totalTasks = someData.count // 用一个串行队列保证计数器操作的线程安全 let counterQueue = DispatchQueue(label: "com.yourApp.taskCounter") someData.forEach { object in self.myFunc(object: object) { success, desiredData in // 处理当前调用的结果 counterQueue.sync { completedTasks += 1 if completedTasks == totalTasks { // 这里就是最后一次调用完成的时机! print("所有异步调用都结束了,包括最后一次") } } } } fileprivate func myFunc(object: ObjectType, completion: @escaping (Bool, DesiredDataType) -> Void) { doesSomeServerAsyncCallWithObjectAndCompletion(object) { success, desiredData in // 处理服务器返回的数据 completion(success, desiredData) } }
方案二:用DispatchGroup(Apple官方推荐的异步任务组)
DispatchGroup是Apple专门为跟踪一组异步任务设计的API,比手动计数器更优雅,不用自己处理线程安全问题:
let taskGroup = DispatchGroup() someData.forEach { object in taskGroup.enter() // 标记一个任务开始 self.myFunc(object: object) { success, desiredData in // 处理当前结果 taskGroup.leave() // 标记任务完成 } } // 监听所有任务完成的回调 taskGroup.notify(queue: .main) { // 所有任务(包括最后一次)都完成了! print("最后一次调用已经结束,全部任务完成") } fileprivate func myFunc(object: ObjectType, completion: @escaping (Bool, DesiredDataType) -> Void) { doesSomeServerAsyncCallWithObjectAndCompletion(object) { success, desiredData in completion(success, desiredData) } }
方案三:Swift 5.5+ 用Async/Await(最简洁的现代写法)
如果你的项目已经升级到Swift 5.5或更高版本,用Async/Await可以让代码更清晰,避免回调地狱:
Task { await withTaskGroup(of: (Bool, DesiredDataType).self) { group in // 把所有异步任务加入任务组 for object in someData { group.addTask { await self.myFuncAsync(object: object) } } // 可以逐个处理每个任务的结果 for await (success, desiredData) in group { // 处理当前结果 } // 当代码走到这里,所有任务(包括最后一次)都已经完成 print("最后一次调用结束,所有任务完成") } } // 把回调式的方法包装成Async/Await风格 fileprivate func myFuncAsync(object: ObjectType) async -> (Bool, DesiredDataType) { return await withCheckedContinuation { continuation in doesSomeServerAsyncCallWithObjectAndCompletion(object) { success, desiredData in continuation.resume(returning: (success, desiredData)) } } }
额外:如何判断最后一次调用发起的时机
如果你想知道什么时候发起最后一次调用,其实很简单——因为forEach是顺序遍历的,当循环处理到数组的最后一个元素时,就是最后一次调用发起的时刻:
guard let lastObject = someData.last else { return } someData.forEach { object in if object === lastObject { // 引用类型用===,值类型用== print("这是最后一次调用发起了!") } self.myFunc(object: object) { success, desiredData in // 处理结果 } }
内容的提问来源于stack exchange,提问作者Ivan Cantarino
相关产品推荐
相关产品推荐

