I came across some unexpected behavior in Combine that i'm hoping someone may be able to explain. I would expect the following code to create an infinite loop, but it actually only runs through the stream once.
let pub1 = PassthroughSubject<Int, Never>()
let pub2 = PassthroughSubject<Int, Never>()
pub1
.handleEvents(receiveOutput: { print("Received new pub1 value: \($0)") })
.combineLatest(pub2)
.handleEvents(receiveOutput: { print("Received new combined value: \($0)") })
.sink { value in
print(value)
pub1.send(value.0)
}.store(in: &subscriptions)
print("sending 1")
pub1.send(1)
print("sending 2")
pub2.send(2)
Generates the following output:
Received new pub1 value: 1
sending 2
Received new combined value: (1, 2)
(1, 2)
Received new pub1 value: 1
Since the value inside pub1 feeds back into itself I would expect sink to be called over and over. What's interesting is that if I get rid of the combineLatest , then this code will create an infinite loop. Something about the combineLatest operator is preventing it and I have no idea what.
I also noticed that adding .receive(on: DispatchQueue.main)
before or after the combineLatest
will also trigger a loop. I guess I'm not understanding something about how threads are handled in Combine. I'm not seeing this non-looping behavior with other operators. For instance merge(with:)
will also create a loop, as expected.