Swift Combine 序列请求执行完第一个就终止无法继续怎么解决?
问题根因
1. Cancellable 提前释放(概率最高)
你第二个请求发不出去的核心原因几乎都是订阅对应的 AnyCancellable 被提前释放了,对应到你的代码逻辑,问题出在请求的完成回调时序:
第一个请求完整的执行顺序是:
- 第一个请求拿到结果,先触发
receiveValue闭包(因为加了receiveOnMain,所以这个闭包被派发到主队列执行),你在这个闭包里调用getData()生成第二个请求的订阅,把第二个请求的AnyCancellable存入了cancellableSet - 紧接着第一个请求的
completion闭包也被派发到主队列执行,如果你在handleCompletion方法里写了cancellableSet.removeAll()这类全局清空的逻辑,刚存进去的第二个请求的Cancellable会被立刻移除释放,对应的请求直接被取消,自然不会发起。
为什么去掉 receiveOnMain 就正常
没加 receiveOnMain 时,sink的两个闭包都会直接在URLSession的后台队列同步执行,要么是你的清空逻辑只在错误分支触发、这时候正常完成不会清空set,要么是时序差异下第二个请求已经发起完成才被释放,所以能看到请求正常执行,只是回调不在主线程报错。
2. 拼写错误
你代码里后两个请求的调度器方法拼写错误:正确写法是 receiveOnMain,你写成了 recieveOnMain(i和e顺序颠倒),如果你的扩展是按正确拼写实现的,写错的方法实际不存在,会导致整个订阅链无效,请求自然不会触发。
3. 调度器使用不规范
receive(on:) 只会影响它之后所有操作符的执行队列,和请求本身的发起队列无关。如果你之前错误的把 receiveOnMain 放在了订阅逻辑之前、导致订阅被派发到主队列执行出现了阻塞,也会出现请求发不出去的问题。
修复方案
- 不要在单个请求的完成回调里全局清空
cancellableSet,可以单独持有每个请求的Cancellable,或者只在页面对象销毁的时候统一清空整个Set。 - 修正所有
receiveOnMain的拼写错误,保证方法被正确调用。 - 不要移除
receive(on: .main)逻辑,它可以保证你的sink闭包都在主线程执行,避免UI操作报错。如果需要优化请求性能,可以在请求Publisher的开头加上subscribe(on: DispatchQueue.global())保证请求的订阅逻辑在后台队列执行。
内容的提问来源于stack exchange,提问作者Vollan
相关产品推荐
相关产品推荐

