Avoid memory leaks when subscribing to RxJS Observables in Angular Components

Share this video with your friends

Send Tweet
Published 3 years ago
Updated 3 years ago

Subscribing to RxJS Observables is a common operation in Angular components. However, we need to pay attention at also cleaning up our subscriptions when our component gets destroyed. This especially holds for those type of Observables that do not immediately complete (like the ones returned from Angular's HttpClient). In this lesson we're going to take a look at different approaches for unsubscribing and how the async pipe can take away a lot of this burden. Also make sure to check out this article on "RxJS: Avoid takeUntil leaks"

Instructor: [00:00] I'm having here a NumberService, which here produces a new observable. Inside that observable, we create here a new interval which on every second emits a new random value on that observer.

[00:12] I also implemented here the disposal logic. Whenever we unsubscribe from the observable, we will get here a console.log, and we will clear our interval. Here I'm having a NumbersComponent -- and the template just visualizes theNumber -- which right now is 42. You can see it here rendered.

[00:31] I also implement here ngOnDestroy. Whenever the component gets destroyed, we get here a console.log statement. Whenever I click that Toggle, it will use an ngIf to hide and remove that component from the DOM. It gets destroyed. We get that message. Once I toggle it again, it get visualized again.

[00:48] What I would like to do here is to use our numbersService and subscribe to its numbers observable. Whenever a new value gets in, I simply assign it here to our number. What you can see now is that on each second a new value gets produced inside here, the DOM element. Let's also here console.log out the value we'll receive.

[01:19] Now, what happens when my component gets destroyed? Let's click the Toggle button. You can see the value doesn't get emitted anymore. We also got that NumbersComponent destroyed, but we continue to get numbers from our observable.

[01:32] The problem is that we have a memory leak here. The observable in our numbersService continues to emit values because the NumberService itself doesn't get destroyed. We have various ways to circumvent in order to prevent that kind of behavior, which we obviously don't want.

[01:47] The simplest one probably is to simply remember theSubscription. I'm assigning here theSubscription, which is returned from the observable to my theSubscription variable. Then in the ngOnDestroy, I'll simply do a this.theSubscription.unsubscribe().

[02:07] If I save this, you can see the values pop up again. Now I click the Toggle button. You can see that the NumbersComponent gets destroyed. Also, the source gets disposed.

[02:19] This is quite simple, especially if you have just one observable here, and you don't have to handle multiple ones. If you'd have to handle multiple ones, then you would have to combine this logic and modify it slightly. There we have an array here. Then, here we iterate over the array as unsubscribe.

[02:34] Our approach, which is quite popular, is to use the takeUntil operator. Let's first of all import here a Subject. We also import the takeUntil operator always from rxjs.

[02:47] Now, at the very top of our component, we create a new Subject. Let's call it destroy. Then in aside our observable here, we use the pipe to apply our takeUntil operator. We tell RxJS, "Take this observable until the destroy one, so the destroy Subject, emits."

[03:11] Obviously, what we also have to do below here is to actually emit. Whenever the OnDestroy callback is called, we emit a new value on our Subject. Then we also complete it. We can also clean up here that logic, which we don't need anymore. We also don't need the variable anymore.

[03:30] Let's check. We receive the values. Now we stop. Again, you see how our subscription is being canceled, is being unsubscribed. We get the source is disposed. We also get that the NumbersComponent has been destroyed.

[03:45] The best way, however, is that you don't even need to subscribe to the observable. We let Angular handle that for us. What we can do is instead of assigning here theNumber, let's make this an observable. Let's also use that suffix here. We simply assign the subscription, which we get here, to that observable up here. Then we render it in our template.

[04:08] We have to use now the async pipe. The async pipe will now take care of the whole subscription/unsubscription process for us when the component gets destroyed.

[04:19] To visualize that, we can remove here the takeUntil, also the subscription, which is not anymore needed. I will add here a tap just to see how the values come in. Now let's save this again. That logic below here is no more needed, so let's remove it. Also, the destroy up here is no more need.

[04:46] The values come in. You can see how we get a Received one, the log statement which we just added here in the tap. Also, the template gets visualized properly. If I toggle, the source is disposed and also this NumbersComponent is being destroyed. You can see how the async pipe handles that completely transparent for us.

[05:03] If you need to have side effects -- which usually you try to avoid, but it's not always possible -- you can always use the tap operator to assign variables inside your component, which you may have done otherwise in your subscription.