How can I emit an item just before my observable stream gets disposed?
According to the specification, you shouldn't emit an item knowing that the subscription has been cancelled:
If a Subscription is cancelled its Subscriber MUST eventually stop being signaled.
So there is no way of doing it with the library, unless you implement your own ObservableOperator.
Related
I'm reading this book about RxSwift "Reactive Programming with Swift 4" by Packt (https://www.oreilly.com/library/view/reactive-programming-with/9781787120211/). In one section, the book says:
"Disposing of a subscription will cause the underlying Observable
sequence to emit a completed event and terminate"
However I found out that the phrase is not correct. Here's an example:
let publisher = PublishSubject<Int>() //Create an <Int> sequence
let subscription1 = publisher.asObservable() //Create 1st subscription
.debug("Subscription 1")
.subscribe { (event) in
print(event)
}
let subscription2 = publisher.asObservable() //Create 2nd subscription
.debug("Subscription 2")
.subscribe { (event) in
print(event)
}
publisher.onNext(1) //Emit first event
subscription1.dispose() //Dispose the 1st subscription. According to the phrase above, publisher should emit an onComplete event
publisher.onNext(2) //Emit second event
As my comment in the code says, at subscription1.dispose(), publisher should've emit an onComplete event, but in fact it can still emit new events as normal, thus the output:
2018-11-01 19:43:59.796: Subscription 1 -> subscribed
2018-11-01 19:43:59.798: Subscription 2 -> subscribed
2018-11-01 19:43:59.798: Subscription 1 -> Event next(1)
next(1)
2018-11-01 19:43:59.798: Subscription 2 -> Event next(1)
next(1)
2018-11-01 19:43:59.799: Subscription 1 -> isDisposed
2018-11-01 19:43:59.799: Subscription 2 -> Event next(2)
next(2)
Can anyone confirm that my understanding in correct? Or did I miss anything behind the scene of RxSwift?
Thanks in advance
Your understanding about unsubscribing is correct: disposing of a subscription does not cause a onComplete to run. However when an onComplete or an onError is reached it will automatically unsubscribe (http://reactivex.io/documentation/contract.html the Subscribing and Unsubscribing section is really useful)
I've just run into this question via Google, so will answer more thoroughly for anyone who might be having similar questions.
The whole misunderstanding here is caused by there being 2 different types of observables:
Cold observables.
Hot observables.
Cold observables are closely related to their subscriptions. They do not initiate any action until they are subscribed to and will terminate as soon as you dispose all of their subscriptions. These are the ones from the book quotation.
Hot observables start whatever they are designed to do as soon as they are created and regardless of their current subscribers. They will always run until their internal action completes, no matter if they have currently 5 subscribers or none. These are the ones from your example.
Always be mindful which type of observable you are currently using. For example cold observable's action may be started separately for each subscriber, so if for example you're dealing with http requests, be careful not to initiate the same request many times by subscribing more than once. On the other hand, the main pitfall with hot observables is exactly in your question - it will not terminate until it's done with it's action.
Hope that can clear up some confusion.
The documentation of Subscription#cancel says that
Data may still be sent to meet previously signalled demand after calling cancel.
In which scenario would people expect the publisher to continue to send till previous signalled demand is met?
Also, if I don't want any new items to be sent after cancellation, what should I do?
Unless you are creating low level operators or Publishers, you don't have to worry about this.
In which scenario would people expect the publisher to continue to send till previous signalled demand is met?
None of the mainstream Reactive Streams libraries do that as they stop sending items eventually. RxJava 2 and Reactor 3 are pretty eager on this so you'd most likely have an extra item on a low-lever asynchronously issued cancellation. Akka Stream may signal more than that (last time I checked, they mix control and item signals and there is a configuration setting for max synchronous items per stream that can lead to multiple items being emitted before the cancellation takes effect).
Also, if I don't want any new items to be sent after cancellation, what should I do?
Depends on what you implement: a Publisher or a Subscriber.
In a Publisher the most eager method is to set a volatile boolean cancelled field and check that every time you are in some kind of emission loop.
In a Subscriber, you can have a boolean done field that is checked in each onXXX so that when you call Subscription.cancel() from onNext, any subsequent call will be ignored.
I am using RXJava to get element from a queue based on a selector. Once I get the first element from the queue, or the message match a specific condition, I want to stop observing on the queue. How can I do it?
ReactiveCamel rx = new ReactiveCamel(camel);
Observable<Message> observable = rx.toObservable("activemq:queue:MyQueue?selector=eventType%3D'custom_message'");
Problem
I have many observables, that periodically emits items.
I need for every running task(observable), running revisor observable, that for example logs execution time, emitted results!, etc.
Example: when user(subscriber) subscribes on observable, revisor observable starts. It's should be transparent to subscribers
How best way to do that?
I want to concatenate a cold and a hot observables. That is, resulting observable should emit the result of cold observable first, then the stuff from the hot one. In the same time, I want to have subscription to the second observable, that is hot, to happen at the same time when subscription to the first one happens, otherwise I miss an important event from it.
That looks very similar to what merge would do. But I want to guarantee that the hot observable will not push anything before the cold one completes, which merge doesn't guarantee. What would be the right way around this?
Use the Replay or PublishLast operators, depending upon your needs. Each has an overload that accepts a selector function.
For example:
var coldThenHot = hot.PublishLast(cold.Concat);
Subscribing to coldThenHot causes PublishLast to invoke the selector first, creating the Concat query. Then it subscribes to it and your hot observable. The last value in the hot observable is buffered. When the cold observable completes, the sequence continues with the buffered value, or simply remains silent until the last value arrives.
However, I'm curious as to what exactly you meant by hot. If your hot observable doesn't generate a value until you subscribe, then technically it's cold. If your observable is truly hot, then you may have already missed the value by the time this query is created. Although, it's possible that it's implicitly buffered already (e.g., if it was created by Observable.FromAsyncPattern), in which case simply concatenate the sequences like normal.
var coldThenHot = cold.Concat(hot);
If you don't want to miss previous data from the hot observable, there is the ReplaySubject that does exactly this : as soon as you subscribe to it, it will push to the subscriber previous elements, which really looks like what you need here.
So what you have to do is subscribe to the cold observable, and when it completes (onCompleted) just subscribe to your ReplaySubject (your hot observable). You have no choice to have some buffering if you need to delay the important data of your hot observable.