switchToLatest是一个非常有意思的Operator,它在我们平时的开发中很常用。它接收publisher,输出具体的值,如下图所示:
仔细观察上图,可以发现,当接收到新的publisher后,switchToLatest
会指向新的publisher,并同时取消之前的publisher。
这一特性非常有用,比如,像下边这样的场景:
随着输入值的不断更新,会不断的触发新的网络请求,在这种情况下,我们想要的是最后一次网络请求,这种场景就是使用switchToLatest
的最佳时机,正如switch to latest的翻译一样,它会切换到latest。
关于switchToLatest
我们只要记住它的两个核心思想就行了:
- 其输入为publihser,输出为具体的值,这就说明它会等待publisher的输出,不管publisher是即时的(
Just(1)
)还是异步的(URLSession.shared.dataTaskPublisher(for: url)
) - 只保留最后的publisher,之前的publisher会自动取消
接下来,我们使用官方的一个例子来进一步讲解。
let subject = PassthroughSubject<Int, Never>()
cancellable = subject
.setFailureType(to: URLError.self)
.map() { index -> URLSession.DataTaskPublisher in
let url = URL(string: "https://example.org/get?index=\(index)")!
return URLSession.shared.dataTaskPublisher(for: url)
}
.switchToLatest()
.sink(receiveCompletion: {
print("Complete: \($0)")
}, receiveValue: { (data, response) in
guard let url = response.url else {
print("Bad response.")
return
}
print("URL: \(url)")
})
for index in 1...5 {
DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(index / 10)) {
subject.send(index)
}
}
打印结果如下:
URL: https://example.org/get?index=5
上边的例子中,我们使用PassthroughSubject
来发送数据,接下来通过map
把1~5转换成网络请求,由于网络请求是有一定延时的,所以只输出了最后发送的数据,因为前边的请求都被取消了,上边的情况正好验证了switchToLatest
的含义。
那么如果上边例子中的publisher不是异步的会怎样呢?
cancellable = subject
.setFailureType(to: URLError.self)
.map() { index -> Just<Int> in
Just(index)
}
.switchToLatest()
.sink(receiveCompletion: {
print("Complete: \($0)")
}, receiveValue: { value in
print("Value: \(value)")
})
for index in 1...5 {
DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(index / 10)) {
subject.send(index)
}
}
打印结果为:
Value: 1
Value: 2
Value: 3
Value: 4
Value: 5
到目前为止,我们基本上讲明白了switchToLatest
的用法,与它相对应的是flatMap
,它返回一个publisher,当该publisher有值的时候,就会流向下游,flatMap
同样是一个非常有意思的Operator。我们把上边的代码稍微改动一下:
let subject = PassthroughSubject<Int, Never>()
cancellable = subject
.setFailureType(to: URLError.self)
.flatMap { index -> URLSession.DataTaskPublisher in
let url = URL(string: "https://example.org/get?index=\(index)")!
return URLSession.shared.dataTaskPublisher(for: url)
}
.sink(receiveCompletion: {
print("Complete: \($0)")
}, receiveValue: { (data, response) in
guard let url = response.url else {
print("Bad response.")
return
}
print("URL: \(url)")
})
for index in 1...5 {
DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(index / 10)) {
subject.send(index)
}
}
打印结果如下:
URL: https://example.org/get?index=5
URL: https://example.org/get?index=1
URL: https://example.org/get?index=2
URL: https://example.org/get?index=3
URL: https://example.org/get?index=4
打印的顺序依赖每个网络请求的速度,速度快的就会先回来。
总结一下,switchToLatest的核心思想是保留最后一个publisher,在实际开发中,特别适合用于过滤搜索框的多余的网络请求。