Swift Combine:如何按顺序运行带超时的Publisher?
解决方案
要实现「publisher1完成后运行带超时的publisher2,且无论publisher2成功、超时或出错都让整个流程结束」的需求,你可以通过捕获超时及其他错误并转换为正常完成事件来处理,具体修改如下:
修改后的代码
// 需声明存储订阅的集合,避免订阅被立即释放 var cancellables = Set<AnyCancellable>() publisher1.flatMap { _ in publisher2 // 为publisher2设置1秒超时,超时后抛出TimeoutError .timeout(1, scheduler: RunLoop.main) // 捕获所有错误(包括超时和Socket自身抛出的错误),转换为正常完成事件 .catch { error in // 可选:打印错误信息用于调试 if error is TimeoutError { print("publisher2 超时") } else { print("publisher2 出错: \(error)") } // 返回空Publisher,触发流的.finished事件 return Empty(completeImmediately: true) } } .handleEvents(receiveCancel: { // 在此添加Socket资源清理逻辑,比如断开连接 print("订阅取消,清理Socket资源") }) .sink( receiveCompletion: { completion in switch completion { case .finished: print("整个流程正常结束") case .failure: // 由于catch已处理所有错误,此处不会触发 break } }, receiveValue: { success in print("收到publisher2返回值: \(success)") } ) .store(in: &cancellables) // 必须存储订阅,否则会被系统立即释放
关键逻辑说明
timeout操作符:给publisher2设置1秒超时阈值,超时后会抛出TimeoutError,若不处理会导致整个流进入失败状态。catch操作符:捕获publisher2产生的所有错误(包括超时和Socket事件可能抛出的错误),返回一个EmptyPublisher——这个Publisher会立即发送.finished事件,让整个流正常结束而非失败终止。- 订阅存储:务必将
sink返回的AnyCancellable存入集合,否则订阅会被系统立即释放,publisher2无法正常执行。 - 资源清理:在
handleEvents(receiveCancel:)中添加Socket断开、资源释放逻辑,避免内存泄漏。
可选扩展:保留错误标识但仍结束流程
如果你希望在sink中区分「正常完成」和「因错误完成」,可以用Result类型包装返回值:
publisher1.flatMap { _ in publisher2 .timeout(1, scheduler: RunLoop.main) .map { Result.success($0) } .catch { error in return Just(Result.failure(error)) } } .sink( receiveCompletion: { _ in print("流程结束") }, receiveValue: { result in switch result { case .success(let value): print("成功收到值: \(value)") case .failure(let error): if error is TimeoutError { print("超时错误") } else { print("其他错误: \(error)") } } } ) .store(in: &cancellables)
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

