使用下面的代码,在我放入receive(on: backgroundQueue)
之后,receiveCompletion
将被称为100%,但receiveValue
块不是。
xxxxPublisher
.xxxx()
.receive(on: backgroundQueue)
.xxxx()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
// completion code
}, receiveValue: { value in
// receive value code
}).store(in: &cancellables)
这似乎不是一个好的行为,我们不应该这样使用receive<S>(on scheduler: S, options: S.SchedulerOptions? = nil)
吗?我错过什么了吗?
下面是重现这个bug的代码,如果你运行这段代码,你会看到receiveCompletion
被调用了正好300次,但是receiveValue
被调用的次数少于300次。
import Foundation
import Combine
private var cancellables: Set<AnyCancellable> = []
let backgroundQueue = DispatchQueue.global(qos: .background)
for i in 1...300 {
runPublisher(i)
}
var sinkCompletedIndices = Set<Int>()
var sinkOutputIndices = Set<Int>()
func runPublisher(_ index: Int) {
[1].publisher
.receive(on: backgroundQueue)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
NSLog("sink receiveCompletion")
sinkCompletedIndices.insert(index)
}, receiveValue: { value in
NSLog("sink receiveValue")
sinkOutputIndices.insert(index)
}).store(in: &cancellables)
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
let diff = sinkCompletedIndices.filter { !sinkOutputIndices.contains($0) }
NSLog("Difference between completions and outputs (diff)")
}
RunLoop.main.run()
问题是Combine
期望调度器作为串行队列操作,但是DispatchQueue。全局是并发的
如果你这样声明后台队列:
let backgroundQueue = DispatchQueue(label: "any name will do", qos: .background)
一切都会如你所愿。
在Swift论坛上有一个有趣的讨论:
https://forums.swift.org/t/runloop-main-or-dispatchqueue-main-when-using-combine-scheduler/26635