在Combine中的flatMap中发布者过早完成
我有这个最小的例子:
import UIKit
import Combine
var values = [1,2,3,4,5]
var cancel = values.publisher
.delay(for: 0.1, scheduler: DispatchQueue.global())
.print()
.flatMap() { i in
[i].publisher.first()
}
.sink { completion in
print("Received Completion: (completion)")
} receiveValue: { v in
print("Received Value: (v)")
}
我的期望是源发布者将 1 到 5 的值发送到流中。每个数字都被转换成(只是为了它)一个新的发布者,它发出第一个值然后完成。由于这是对每个数字完成的,我希望所有值都到达接收器。然而,情况并非如此。输出如下所示:
request unlimited
receive value: (1)
Received Value: 1
receive value: (2)
Received Value: 2
receive value: (4)
Received Value: 4
receive finished
Received Completion: finished
receive value: (3)
receive value: (5)
实际上,在完成事件到达之前,只有 3 个值到达接收器。为什么是这样?该文件指出:
新发布者的成功完成并没有完成整个流。
更奇怪的,当你更换.flatMap()了.flatMap(maxPublishers: .max(1)),并添加一个.share()原始的源出版商只有第一个值,使得它下沉。
任何指针都非常感谢!
回答
你的使用DispatchQueue.global()是问题。所述values.publisher发送所有其输出,和它的完成,向下游delay操作者尽快values.publisher接收订阅。的delay操作者的时间表六个块(5用于输出数字和一个用于完成)上运行DispatchQueue.global()以后0.1秒。
DispatchQueue.global()是并发队列。这意味着它可以同时运行任意数量的块。因此无法保证六个预定区块中的哪一个将首先完成。
将并发队列用作组合调度程序通常是一个坏主意。您应该改用串行队列。这是一个简单的例子:
var cancel = values.publisher
.delay(for: 0.1, scheduler: DispatchQueue(label: "my queue"))
.print()
.flatMap() { i in
[i].publisher.first()
}
.sink { completion in
print("Received Completion: (completion)")
} receiveValue: { v in
print("Received Value: (v)")
}
但是您可能希望创建一次队列并将其存储在一个属性中,这样您就可以将它与多个发布者一起使用。
如果您确实希望将输出传送到主队列(可能是因为您要使用它们来更新视图),则应该使用DispatchQueue.main.