Cancelling Observables — RxJS
RxJS is something I have come to appreciate quite a bit in the recent past, in it’s ability to manage asynchronous operations. It really is a shift in the programing paradigm of how you look at asynchronous code. If it is adopted consistently well across your app, you can see a significant improvement in how you manage asynchronous code. In this post I want to talk about a particular principle of Observables. This post wouldn’t make a lot of sense if you have not had exposure to RxJS.
Unsubscribing or Cancelling.
One of the key features of Observables is its ability to cancel the underlying operation of the source. In fact the ReactiveX documentation explains the primary purpose of the
Subscription as this.
Subscription: represents the execution of an Observable, is primarily useful for cancelling the execution.
However, if you have been using RxJS for long enough, this is not a concept that seems natural to
Observables. The pattern seems to almost dictate that
Subscriptions are simply a “viewership” to the source and not something that controls the source. This is mostly because of the different types of Observables that we come to use and our inability to always know what type an observable is and what really happens when you do
unsubscribe from the
One of the key concepts that brings inconsistency to how you look at an observable instance is it’s temperature. An Observable can be hot or cold or sometimes even warm. I don’t intend to discuss Observable temperature in this post, even though it is key for understanding the rest of this post. Mostly because this is something discussed quite elaborately and this post by Thoughtram does a excellent job at it.
Once you have read the above post, you will also learn that there are operators that you can use to change the temperature of an Observable. Particularly operators like
Affecting the underlying operation of a hot observable is not possible as you may have already understood. This is because the source has no control over the underlying operation. So when you
subscribe you are essentially just starting to listen. Similarly when you
unsubscribe you are stopping your listening. Following is a classic example of a hot observable.
let source = fromEvent( element, ‘mousemove’ );
In the following example we create a hot observable using the
create operator. This might look like a bit of an overkill but it’s purely for the purpose of explaining a hot observable’s attachment to it’s underlying operation.
As you can see the ticker starts to fire every 100ms as the code starts to execute. The observable returned by the
getTicks emits each tick to the subscriber. The observable always gives every observer what the current tick is regardless of when the observer subscribed. By unsubscribing you essentially stop listening, but the ticker continues to work.
Cold observables on the other hand can affect the underlying operation specific to the observer. On cold observables, the observer starts the underlying operation for that observer with a
subscribe. Similarly the observer ends the underlying operation for that observer with an
unsubscribe. Most creation operators create a cold observable.
let source = range( 1, 10 );
The above observable emits values from 1 to 10 when an observer subscribes. For every other observer that subscribes the values are emitted again. Lets explore this further with the ticker example that uses only the
create operator to create a cold observable.
In this case the ticker does not start until an observer subscribes to
getTicks observable. Every time an observer subscribes to the observable a new ticker is created that emits from 0. When an observer unsubscribes, the underlying ticker specific to that observer is stopped. This is identical to
The ticker example shows when you
unsubscribe from a cold observable source, you essentially affect the source operation specific to that observer/subscription.
Warm observables are observables whose temperature change between hot and cold. Thoughtram’s post explains some cases of observables which are warm. There are many different cases of warm observables. Lets take the case of a cold observable that turns hot when subscribed and turns cold again when all observers unsubscribe from it. This can be achieved using some operators as follows.
let source = interval(100).pipe( publish(), refCount());
In this case
interval produces a cold observable but
publish (along with
connect operator) makes it a hot observable. The
refCount makes it warm — initially cold but turns hot when the first subscription occurs. Lets use the ticker example with
create operator to make a warm observable that behaves similarly.
In this particular case, the observers fully control the underlying operation of the observable. By creating the first subscription we are able to start the ticker and by unsubscribing all subscriptions we are able to stop the ticker.
Observables do give control to the subscriber to manage the underlying operation, but only in some cases. This is not great — specifically if the subscriber has no idea as to what type the observable is and what it stands for. So this is something to watch out for and handle on a case by case basis. If used wrongly this particular aspect of observables can lead to issues that are difficult to trace. Nevertheless, RxJS and Observables have become a great platform for managing asynchronous code and taking it to the next level.