Combine里面类似flatMapLatest的操作方法
在 Combine 框架中,并没有一个名为 flatMapLatest 的直接操作符,这是 ReactiveX(例如 RxSwift)中的一个术语,用于描述一个特定的 flatMap 行为:每当源发布者发出一个新的值,flatMapLatest 会切换到新的发布者,并取消订阅之前的发布者。
在 Combine 中,要实现 flatMapLatest 的行为,可以使用 map 和 switchToLatest 操作符结合起来。switchToLatest 是 Combine 提供的操作符,用于只接收最新发布者发出的值,忽略旧的发布者的值。
以下是一个示例,演示如何模拟 flatMapLatest 的行为:
var cancellables = Set<AnyCancellable>()
// 返回 Publisher 的函数
func publisher(for value: Int) -> AnyPublisher<Int, Never> {
// 这里我们创建一个将 Int 值封装在 Publisher 中的 Publisher
return Just(value)
.delay(for: .seconds(Double(value)), scheduler: RunLoop.main)
.eraseToAnyPublisher()
}
// 源 Publisher,可以是任何类型的 Publisher
let sourcePublisher = PassthroughSubject<Int, Never>()
// 使用 map 来处理每个发送的值,并确保在任何时刻只有一个内部 Publisher 被订阅
sourcePublisher
.map { value -> AnyPublisher<Int, Never> in
return publisher(for: value)
}
.switchToLatest() // 只订阅最新的内部 Publisher
.sink(receiveCompletion: { completion in
print("Completed with: \(completion)")
}, receiveValue: { value in
print("Received value: \(value)")
}).store(in: &cancellables)
sourcePublisher.send(1)
sourcePublisher.send(2) // 1 的结果会被忽略,因为 2 是最新的
sourcePublisher.send(3) // 2 的结果会被忽略,因为 3 是最新的
在这个例子中,sourcePublisher 是一个 PassthroughSubject,它可以发送值。每当它发送一个新的值,map 操作符会根据这个值创建一个新的 Publisher。然后,switchToLatest 只会订阅最新的 Publisher,之前的订阅会被取消,从而模拟出 flatMapLatest 的行为。
使用 map 和 switchToLatest 的组合是 Combine 中实现 flatMapLatest 的方式,我们也可以写一个flatMapLatest扩展操作方法。
extension Publisher {
func flatMapLatest<T: Publisher>(_ transform: @escaping (Output) -> T) -> Publishers.SwitchToLatest<T, Publishers.Map<Self, T>> where T.Failure == Failure {
return map(transform).switchToLatest()
}
}
这样的话可以把之前的代码简化成:
// 使用 map 来处理每个发送的值,并确保在任何时刻只有一个内部 Publisher 被订阅
sourcePublisher
.flatMapLatest { value -> AnyPublisher<Int, Never> in
return publisher(for: value)
}
.sink(receiveCompletion: { completion in
print("Completed with: \(completion)")
}, receiveValue: { value in
print("Received value: \(value)")
}).store(in: &cancellables)