Swift Combine 结合 Clean Architecture 异步任务执行顺序异常排查
问题成因
- Combine 中的
Future属于热发布者,实例创建时就会同步执行其构造闭包,不需要等待订阅动作触发。 - 默认情况下
Future的构造闭包会在当前调用线程(此处为主线程)同步执行,闭包中访问task计算属性时,会同步触发repository.test()里的sleep(3)阻塞当前线程,直到 sleep 结束、执行 promise、sink 收到值打印 1 之后,才会执行到后续的print("Started")逻辑,所以输出顺序和预期相反。 - 额外隐患:当前你把
cancellables声明为 init 方法的局部变量,init 执行完毕后变量就会被销毁,存储的订阅会被立即取消,就算修改为异步执行也可能收不到回调。
解决方案
需要做两处核心修改:
- 给
Future指定后台队列执行任务,避免阻塞主线程,保证print("Started")先执行 - 将
cancellables提升为类/结构体的成员属性,延长订阅生命周期
修改后的代码示例如下:
修改 UseCaseBase 的 execute 方法
func execute(with payload: TInput) -> AnyPublisher<TOutput, Never> { return Future<TOutput, Never> { promise in promise(.success(self.task!)) } // 指定任务在后台全局队列异步执行 .subscribe(on: DispatchQueue.global()) // 如果后续回调需要操作UI,可加这行切回主线程,不需要可省略 .receive(on: DispatchQueue.main) .eraseToAnyPublisher() }
修改 ContentView 的属性声明
struct ContentView: View { // 提升为成员属性,生命周期和View保持一致 private var cancellables = Set<AnyCancellable>() init() { let useCase = MockUseCase(MockRepository()) useCase.execute(with: "String") .sink(receiveValue: { value in print(value) }) .store(in: &cancellables) print("Started") } var body: some View { Text("Hello World") } }
修改后调用 execute 时,Future 的任务会被提交到后台队列异步排队,不会阻塞当前主线程,会立刻执行后续的 print("Started");后台队列执行任务时会先 sleep 3秒,完成后再触发 sink 打印 1,符合预期的输出顺序。
内容的提问来源于stack exchange,提问作者Allan Snicker
相关产品推荐
相关产品推荐

