异步依赖任务执行异常:OperationQueue下任务并行且回调提前触发
问题根源分析
我一眼就看出问题所在了——你用BlockOperation包裹了异步API调用,但BlockOperation的核心逻辑是只要闭包里的同步代码执行完,就立刻标记自己为完成状态,它根本不会等待内部的异步task1Call/task2Call真正结束!
这就导致了诡异的输出顺序:
- t1的
BlockOperation刚执行完TaskManager.shared.task1Call { ... }这行代码(只是启动了异步任务),就直接标记自己完成了 - t2的依赖条件(t1完成)瞬间满足,于是它也启动自己的异步任务后立刻标记完成
- successBlock的依赖(t1、t2都完成)也随之满足,直接执行打印
Tasks Completed,但此时真正的T1、T2后台任务还在慢悠悠跑着,所以才会出现任务还在执行、成功回调却先触发的情况。
解决方案
针对异步任务的依赖管理,我给你两个最实用的实现方案,按需选择:
方案1:自定义异步Operation(推荐,贴合OperationQueue设计)
系统自带的BlockOperation不支持异步任务状态管理,咱们自己写一个支持手动控制完成状态的基类,再基于它实现任务:
// 自定义异步Operation基类,处理状态同步 class AsyncOperation: Operation { private let lockQueue = DispatchQueue(label: "com.task.async.lock") override var isAsynchronous: Bool { true } private var _isExecuting: Bool = false override var isExecuting: Bool { get { lockQueue.sync { _isExecuting } } set { willChangeValue(forKey: "isExecuting") lockQueue.sync { _isExecuting = newValue } didChangeValue(forKey: "isExecuting") } } private var _isFinished: Bool = false override var isFinished: Bool { get { lockQueue.sync { _isFinished } } set { willChangeValue(forKey: "isFinished") lockQueue.sync { _isFinished = newValue } didChangeValue(forKey: "isFinished") } } override func start() { guard !isCancelled else { finish() return } isExecuting = true main() } override func main() { fatalError("子类必须重写main方法实现异步任务逻辑") } func finish() { isExecuting = false isFinished = true } } // 基于基类实现T1、T2任务 class Task1Operation: AsyncOperation { override func main() { TaskManager.shared.task1Call { print("Task 1 Completed") self.finish() // 异步任务真正完成后,才标记Operation结束 } } } class Task2Operation: AsyncOperation { override func main() { TaskManager.shared.task2Call { print("Task 2 Completed") self.finish() } } } // 修改执行类的方法 class Execution { func executeTaskWithOperation() { let t1 = Task1Operation() let t2 = Task2Operation() let successBlock = BlockOperation { print("Tasks Completed") } let oper = OperationQueue() t2.addDependency(t1) // T2会等T1真正完成才启动 successBlock.addDependency(t1) successBlock.addDependency(t2) oper.addOperations([t1, t2, successBlock], waitUntilFinished: true) } }
这个方案的核心是手动控制Operation的执行状态,只有当异步任务的回调触发时,才标记Operation完成,这样依赖关系才会真正生效。
方案2:用DispatchGroup管理(更轻量,适合简单场景)
如果不想自定义类,用DispatchGroup可以更简洁地实现需求,还能直接处理T2依赖T1输出的逻辑:
class Execution { func executeTaskWithDispatchGroup() { let group = DispatchGroup() // 执行T1,完成后启动T2 group.enter() TaskManager.shared.task1Call { print("Task 1 Completed") // 这里可以获取T1的输出数据,直接传给T2 let t1Output = "从T1拿到的关键数据" // T1完成后启动T2 group.enter() TaskManager.shared.task2Call { print("Task 2 Completed(使用了T1输出:\(t1Output))") group.leave() } group.leave() } // 等待所有任务完成后,在主线程更新UI group.notify(queue: .main) { print("Tasks Completed") // 这里写你的UI更新逻辑 } } }
这个方案更轻量化,天然支持异步任务的依赖传递,还能直接在T1的回调里把数据传给T2,完美匹配你“T2依赖T1输出”的需求。
验证效果
不管用哪种方案,最终的输出都会变成符合预期的顺序:
~~> Task 1 Executing .. 0 ~~> Task 1 Executing .. 1 ... ~~> Task 1 Executing .. 10 Task 1 Completed ==> Task 2 Executing .. 0 ==> Task 2 Executing .. 1 ... ==> Task 2 Executing .. 10 Task 2 Completed Tasks Completed
内容的提问来源于stack exchange,提问作者200_OK
相关产品推荐
相关产品推荐

