Combine扩展问题:如何实现每次事件触发时的概率性失败
Combine 实现每次事件触发时随机失败的扩展
问题根源
你之前的扩展只在Publisher创建时执行一次概率判断,大概率是因为你把随机逻辑写在了扩展的顶层(比如直接在初始化阶段就完成了判断),没有绑定到每个上游事件的处理流程里。这种情况下,只有第一次创建Publisher时会跑一次随机逻辑,后续所有事件都会复用这个结果。
正确的操作符:flatMap
要实现“每个事件触发时都重新判断概率”,得用flatMap操作符。它的核心作用是为上游的每一个输出事件创建一个新的子Publisher,这样每次事件到来时,你都能重新执行随机判断逻辑,决定返回原事件还是失败信号。
代码示例
错误写法(仅执行一次判断)
extension Publisher { func occasionallyFail(with probability: Double) -> AnyPublisher<Output, Error> { // 这里的随机判断只在扩展被调用时执行一次 let shouldFail = Double.random(in: 0...1) < probability if shouldFail { return Fail(error: NSError(domain: "MockError", code: 0, userInfo: nil)) .eraseToAnyPublisher() } else { return self.eraseToAnyPublisher() } } }
正确写法(每次事件触发都判断)
extension Publisher where Failure == Error { func occasionallyFail(with probability: Double) -> AnyPublisher<Output, Error> { return self.flatMap { value in // 每个事件到来时都会执行这个随机判断 let shouldFail = Double.random(in: 0...1) < probability if shouldFail { return Fail(error: NSError(domain: "MockLoaderError", code: 1, userInfo: [NSLocalizedDescriptionKey: "随机加载失败"])) .eraseToAnyPublisher() } else { return Just(value) .setFailureType(to: Error.self) .eraseToAnyPublisher() } } .eraseToAnyPublisher() } }
代码说明
- 用
flatMap包裹每个上游输出:每当上游发送一个事件,flatMap就会执行一次内部的随机判断逻辑。 - 判断失败时返回
FailPublisher,直接发送失败信号;成功时用Just把原事件重新包装成Publisher,保持事件流的连续性。 - 约束
Failure == Error,确保扩展只适用于失败类型为Error的Publisher,避免类型不匹配问题。
测试验证
你可以用定时发送事件的Publisher来测试效果:
var cancellables = Set<AnyCancellable>() let timer = Timer.publish(every: 1, on: .main, in: .common) .autoconnect() .map { _ in "加载数据成功" } .setFailureType(to: Error.self) .occasionallyFail(with: 0.3) // 30%概率失败 timer.sink( receiveCompletion: { completion in if case .failure(let error) = completion { print("失败:\(error.localizedDescription)") } }, receiveValue: { value in print("成功:\(value)") } ).store(in: &cancellables)
运行后会看到,每一秒钟都会重新判断一次是否失败,而不是只在timer创建时判断一次。
内容的提问来源于stack exchange,提问作者Marcin Kapusta
相关产品推荐
相关产品推荐

