首页 > 解决方案 > collect() 方法的不明显行为

问题描述

private lazy var dispatchQueue = DispatchQueue(
        label: "\(type(of: self))",
        attributes: .concurrent
    )

func makeArrayGreatAgain(elem0: SomeStruct, elem1: SomeStruct, elem2: SomeStruct, elem3: SomeStruct) {
[elem0 elem1, elem2, elem3]
.publisher
.receive(on: dispatchQueue)
.tryMap { print("R", $0) } // Void
.eraseToAnyPublisher() // just copy-pasted
.collect(4)
.sink { result in print("E", result) }
 receiveValue: { result in print("S", result) }
}

我不时看到不同的日志。

例如:

R
R
R
S [(), (), ()]
E
R

或者

R
R
R
R
S [(), (), (), ()]
E

为什么?我想获得整个结果:

    R
    R
    R
    R
    S [(), (), (), ()]
    E

我怎样才能做到这一点?

评论

collect()没有参数也会出现同样的问题

标签: swiftcombine

解决方案


在您的示例中,这与在并发队列上接收的.collect()所有内容无关。receive(on:)

因此,值和(对于这个问题至关重要)完成信号是同时处理的,因此它们发出的顺序与它们到达的顺序不同。

因此,有时,完成信号实际上会在所有值到达之前到达下一步。

如果下一步是collect发布者,当它看到完成时,它会发出一个它在该点之前收到的值的数组,并且也会完成。


为确保您按顺序接收值,请使用串行队列:

var serialQ = DispatchQueue(
    label: "foo"
)

let c = ["1", "2", "3", "4"]
   .publisher
   .receive(on: serialQ) // or receive(on: DispatchQueue.main)
   .map { print("R", $0) }
   .collect()
   .sink (receiveCompletion: { print("E", $0) },
          receiveValue: { print("S", $0) })

你将永远得到:

R 1
R 2
R 3
R 4
S [(),(),(),()]
E finished 

推荐阅读