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

是否有符合Combine风格的方案可阻止多个请求被重复触发?

符合Combine范式的防重复请求实现方案

核心思路是完全移除外部的isFetching可变状态,所有状态逻辑封装在Combine流内部,避免手动维护状态带来的不同步风险,代码更简洁易维护。

场景1:新请求触发时自动取消旧请求,仅执行最新请求

适合需要以最新触发的请求为准的场景,使用flatMap(maxPublishers: .max(1))(即flatMapLatest)的特性实现:

private let fetchTrigger = PassthroughSubject<Void, Never>()
private var cancellables = Set<AnyCancellable>()

init() {
    fetchTrigger
        // 同一时间最多保持1个活跃的内部请求Publisher
        .flatMap(maxPublishers: .max(1)) { [weak self] _ -> AnyPublisher<你的接口输出类型, 你的接口错误类型> in
            guard let self = self else { return Empty().eraseToAnyPublisher() }
            // Deferred保证只有订阅时才会真实发起请求
            return Deferred { self.apiPublisher }
                .eraseToAnyPublisher()
        }
        .receive(on: .main)
        .assign(to: \.foo, on: self)
        .store(in: &cancellables)
}

// 原fetch方法仅需要发送触发事件
func fetch() {
    fetchTrigger.send()
}

场景2:新请求触发时若已有请求在运行,直接丢弃新请求

适合需要保证正在进行的请求不被打断的防重入场景,使用scan操作符在流内部维护请求状态:

private let fetchTrigger = PassthroughSubject<Void, Never>()
private var cancellables = Set<AnyCancellable>()

init() {
    fetchTrigger
        // 内部维护请求运行状态
        .scan((isRunning: false, shouldFetch: false)) { state, _ in
            state.isRunning ? (true, false) : (true, true)
        }
        // 仅当无运行中请求时才继续执行
        .filter(\.shouldFetch)
        .flatMap { [weak self] _ -> AnyPublisher<你的接口输出类型, 你的接口错误类型> in
            guard let self = self else { return Empty().eraseToAnyPublisher() }
            return self.apiPublisher
                .handleEvents(receiveCompletion: { [weak self] _ in
                    // 请求完成后发送事件重置状态
                    self?.fetchTrigger.send()
                })
                .eraseToAnyPublisher()
        }
        .receive(on: .main)
        .assign(to: \.foo, on: self)
        .store(in: &cancellables)
}

func fetch() {
    fetchTrigger.send()
}

轻量场景简化方案

如果仅需要短时间内多次调用fetch时共享同一份请求结果,不需要严格的状态控制,可以使用share()操作符实现请求共享:

private lazy var sharedApiPublisher: AnyPublisher<你的接口输出类型, 你的接口错误类型> = {
    apiPublisher
        .receive(on: .main)
        .share()
        .eraseToAnyPublisher()
}()

func fetch() {
    sharedApiPublisher
        .assign(to: \.foo, on: self)
        .store(in: &cancellables)
}

内容的提问来源于stack exchange,提问作者Fogmeister

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 03:54:07