在Combine框架中,有一个demand的概念,它允许向publishers发出背压信号。
假设我有一个简单的发布者:
let numbers = Publishers.Sequence<ClosedRange<Int>, Error>(sequence: 0...100)
我想下载某些使用这些数字作为参数的URL。我还希望下一次下载仅在上一次下载完成后才能开始。
那么幼稚的方法看起来像这样:
let subscription = numbers.sink(receiveCompletion: { _ in }, receiveValue: {
let url = URL(string: "https://httpbin.org/get?value=\($0)")!
URLSession.shared.dataTask(with: url) {
$0.map { print(String(data: $0, encoding: .utf8)!) }
}.resume()
})
不幸的是,这不能满足在开始下一个下载之前等待上一个下载完成的要求。据我所知,sink
函数将返回类型为AnyCancellable
的值,而不是类型为Subscription
的值。如果是后者,我们可以在上传完成后根据特定需求在subscription
上调用request
函数。
控制sink
或任何其他标准合并Subscriber
提供的订阅需求的最佳方法是什么?
答案 0 :(得分:2)
结果是,flatMap
运算符采用了附加的maxPublishers
参数,该参数采用了Subscribers.Demand
的值。与Future
发布者结合使用,这使得numbers
发布者可以等到将来能够处理给定值,然后再发送下一个值。
将其应用于原始代码,一个接一个地下载值,如下所示:
enum DownloadError: Error {
case noData
}
let subscription = numbers.flatMap(maxPublishers: .max(1)) { number in
Future { promise in
let url = URL(string: "https://httpbin.org/get?value=\(number)")!
URLSession.shared.dataTask(with: url) {
switch ($0, $2) {
case let (data?, nil):
promise(.success(data))
case let (nil, error?):
promise(.failure(error))
default:
promise(.failure(DownloadError.noData))
}
}.resume()
}
}.sink(
receiveCompletion: { _ in print("errors should be handled here") },
receiveValue: { print(String(data: $0, encoding: .utf8)!) }
)