是否有符合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
相关产品推荐
相关产品推荐

