Swift async/await & Task:如何实现并发任务的进度回调?
问题原因分析
1. 未真正实现并发执行
如果你的代码是顺序调用await每个任务,而非用TaskGroup或同时启动多个Task,任务会串行执行,完全不会按耗时顺序完成。比如这种错误写法:
func processTasks() async { let result1 = await task1() updateProgress(result1) let result2 = await task2() updateProgress(result2) let result3 = await task3() updateProgress(result3) }
这种情况下任务会挨个执行,没有并发可言,进度回调自然按调用顺序触发,和任务耗时无关。
2. 进度回调被串行队列阻塞
如果updateProgress方法在主线程执行,而主线程此时有其他耗时操作,或者回调本身执行时产生阻塞,会导致进度打印时机延迟。比如回调里做了同步UI操作,但主线程被占用,就会出现打印时机不符合预期的情况。
3. 任务优先级设置不合理
如果给不同任务设置了差异较大的优先级,系统调度器可能优先执行高优先级任务,哪怕它耗时更长。比如给耗时7秒的任务设为.userInitiated优先级,而2秒的任务是.background,系统可能先调度前者,导致完成顺序和预期不符。
正确的并发实现示例
要实现真正的并发并及时触发进度回调,应该用TaskGroup同时启动所有任务,每个任务完成后立即调用回调:
func processConcurrentTasks() async { await withTaskGroup(of: TaskResult.self) { group in // 同时添加所有任务到任务组 group.addTask { await self.task1() } group.addTask { await self.task2() } group.addTask { await self.task3() } // 遍历已完成的任务,一旦有任务完成就立即处理 for await result in group { self.updateProgress(result) } } } // 模拟耗时任务 func task1() async -> TaskResult { try? await Task.sleep(nanoseconds: 4_000_000_000) return TaskResult(id: 1, message: "任务1完成") } func task2() async -> TaskResult { try? await Task.sleep(nanoseconds: 7_000_000_000) return TaskResult(id: 2, message: "任务2完成") } func task3() async -> TaskResult { try? await Task.sleep(nanoseconds: 2_000_000_000) return TaskResult(id: 3, message: "任务3完成") } struct TaskResult { let id: Int let message: String } func updateProgress(_ result: TaskResult) { print(result.message) }
这个实现里,三个任务会同时启动,哪个先完成就会先进入for await循环,立即触发进度回调,符合你预期的完成顺序。
额外注意点
Task.sleep会挂起当前任务但不会阻塞线程,是模拟耗时操作的正确方式;- 如果回调涉及UI更新,需要确保在主线程执行,可以用
MainActor.run包裹:func updateProgress(_ result: TaskResult) { Task { @MainActor in print(result.message) // 这里可以安全更新UI } }
内容的提问来源于stack exchange,提问作者John DoeDoe
相关产品推荐
相关产品推荐

