You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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就会执行一次内部的随机判断逻辑。
  • 判断失败时返回Fail Publisher,直接发送失败信号;成功时用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 08:00:03