PassthroughSubject sends values out of order

I've found a situation in which PassthroughSubject delivers its values in the wrong order. Is this is working as designed or a bug?

let subject = PassthroughSubject<String, Never>()
let loggedSubject = subject.print()

let printer = loggedSubject.sink {
    print("printer: \($0)")
    if $0 == "Manual message" {
        subject.send("Automatic message")
    }
}

let printer2 = loggedSubject.sink {
    print("printer2: \($0)")
}

subject.send("Manual message")

When this code is run it prints:

receive subscription: (PassthroughSubject)
request unlimited
receive subscription: (PassthroughSubject)
request unlimited
receive value: (Manual message)
printer: Manual message
receive value: (Automatic message)
printer: Automatic message
receive value: (Automatic message)
printer2: Automatic message
receive value: (Manual message)
printer2: Manual message

It makes sense to me that printer2 receives all its values after printer. However, I was surprised that printer2 receives its values in the wrong order.

1 Like

the send which you invoke on last line is executed sequentially (synchronized) meaning the first subscription will be called with send and the automatic send in the sink followed immediately with automatic send to the second one before the second subscription received the "manual message".
Combine performs stuff sequently unless you have custom schedulers.

Ah yes that's really helped my understanding. Thank you @filiplazov :grin:

If I change the loggedSubject to send values on a serial queue then I get the behaviour I was originally expecting.

I changed loggedSubject as below

let loggedSubject = subject
    .print("Sequential")
    .receive(on: DispatchQueue(label: "queue"))
    .print("Queued")

and got the output:

Sequential: receive subscription: (PassthroughSubject)
Queued: receive subscription: (ReceiveOn)
Queued: request unlimited
Sequential: request unlimited
Sequential: receive subscription: (PassthroughSubject)
Queued: receive subscription: (ReceiveOn)
Queued: request unlimited
Sequential: request unlimited
Sequential: receive value: (Manual message)
Sequential: receive value: (Manual message)
Queued: receive value: (Manual message)
printer: Manual message
Sequential: receive value: (Automatic message)
Sequential: receive value: (Automatic message)
Queued: receive value: (Manual message)
printer2: Manual message
Queued: receive value: (Automatic message)
printer: Automatic message
Queued: receive value: (Automatic message)
printer2: Automatic message

Staring at that for a while helped me understand how this all works!

Thanks again

1 Like

It takes some tinkering but it is quite simple after you have some experience with it :)