Using the RxJS library means dealing with observables on a regular basis. You must have heard the following cliche hundreds of times:
If you subscribe to an observable, you need to take care of unsubscribing when you’re no longer interested in receiving notifications, otherwise you will create a memory leak.
There’re already several common techniques/approaches to handle the problem, however I’d like to introduce you to yet another one 🆕 in this blog post .
State of art
For the time being, you can pick one of the following solutions:
Dealing with subscriptions on your own
You can simply subscribe to an observable and keep a reference to the subscription under a class field. In the ngOnDestroy lifecycle hook method you need to call the unsubscribe method for all Subscription instances.
- the easiest to understand solution for newbies,
- no additional dependencies.
- hard to maintain for large number of subscriptions,
- easy to forget implementing the ngOnDestroy method,
- before calling the unsubscribe method, you need to check if a subscription has been defined.
Using add method of a Subscription instance
You can create a parent Subscription instance and add child subscriptions to it using the add method. Although it’s simple and straightforward solution, it’s not well known.
- keep multiple subscriptions under a single class field,
- call the unsubscribe method on a single object only in the ngOnDestroy method,
- no need to check if a subscription has been defined (it’s done at the moment of object creation),
- no additional dependencies.
- easy to forget implementing the ngOnDestroy method.
You can also take a similar approach and store subscriptions in an array and further call the forEach method in the ngOnDestroy method, however there’s no point in reinventing the wheel. In addition, there is a SubSink solution available.
Another way to mange subscriptions is to use a third-party solution based on decorators. The most popular is the AutoUnsubscribe. You simply subscribe and assign a Subscription instance to a class field. The decorator takes care of calling the unsubscribe method for all Subscription instances when the ngOnDestroy method is invoked for a component.
- no need to unsubscribe on your own,
- no need to check if a subscription has been defined.
- additional dependency,
- need to assign each Subscription instance to a class field,
- need to always implement OnDestroy interface.
Using Async Pipe in Angular
This is my favorite solution when it comes to dealing with the RxJS in an Angular application. Since most of the times you subscribe in order to get data for a view to render, you can subscribe implicitly at the view level. The Async Pipe takes care of unsubscribing when a component gets destroyed, therefore you will not get into memory leak troubles.
- no need to subscribe explicitly, therefore not need to unsubscribe,
- no need to store references to Subscription instances in class fields,
- no need to implement the OnDestroy interface,
- declarative/reactive solution as opposed to imperative code of the aforementioned approaches,
- no additional dependencies.
- harder to access emitted values in a component’s methods.
Make OOP great again
So I’ve come up with another imperative solution which allows you to keep data emitted by an observable in a class field and helps you to avoid memory leaks.
The idea is to make use of inheritance and simply expose a single protected method for concrete classes extending the abstract one. More precisely two protected methods, but I’ll explain it in a moment:
As you can see the abstract class is responsible for subscribing to an observable and keeps the resulting Subscription object in its private store. I simply make us of a single parent Subscription instance and add child subscriptions to it, but you could take different approaches as well, namely array-based or using the SubSink third-party class. When a component which extends the abstract class gets destroyed, the ngOnDestroy method from the abstract class is invoked and all the subscriptions are cancelled.
Let’s see it in action:
In the above component I simply make use of inheritance in order to get the subscribe method from the base class. You can test the solution, since I provide a link to the example at the end of the blog post.
The counter component doesn’t keep reference to the underlying subscription, since it’s desired to keep the subscription alive until it gets destroyed. However, there’re situations when you need to be able to cancel some subscriptions on demand. Therefore, you need to keep a reference to it in order to call the unsubscribe method for a given subscription when needed. The WithSubscription base class takes the scenario into account and returns a reference to the underlying Subscription instance from the subscribe method. Then, in order to unsubscribe on demand, you simply need to call the base class unsubscribe method and pass a reference to an appropriate Subscription instance:
Note that keeping a reference to the Subscription instance is only needed if you want to be able to cancel it on demand.
- no need to unsubscribe (explicitly in your code),
- no need to store references to Subscription instances in class fields (explicitly in your code),
- no need to implement the ngOnDestroy method (explicitly in your code).
- need to extend the base class,
- additional dependency.
In this blog post I presented another alternative to mange the RxJS subscriptions in an imperative way. Although my first choice will always be the Async Pipe based approach, I agree that for some people it can be initially difficult to think in a reactive way and they may prefer to follow the imperative solutions. If you want an in-depth comparison I highly encourage you to take a look at this great blog post by Tomas Trajan. In addition, you don’t always use the RxJS together with Angular. Feel free to play around with the example:
I hope you liked the post and learned something new 👍 If so, please give me some applause 👏