Preventing Memory Leaks in Angular Observables with ngOnDestroy
Time to read: 3 minutes
A memory leak is one of the worst types of issues you can have. It’s hard to find, hard to debug, and often hard to solve. Unfortunately, this problem occurs in every programming language or framework, including Angular. Observables are awesome, because of the incessant stream of data, but this benefaction can cause serious problem with memory leak. Today we will take a closer look at the ngOnDestroy
Angular hook, and answer the question: “When should I unsubscribe from an observable? What is the best pattern to use?”
In this post we will:
- Create an app which generates random numbers.
- Reproduce a memory leak in it.
- Fix the memory leak with the
takeUntil
+ngOnDestroy
pattern.
To accomplish these tasks, make sure you:
- Install Node.js and npm (at the time of writing I am using Node.js v8.11.1 and npm 5.8.0).
- Install @angular/cli (in this post I am using version 6.0.0).
- Have intermediate knowledge of Angular.
Let’s Create the App
We need to start by initializing a new Angular project. To do that, enter the following commands in your terminal:
The ng new
command will initialize a new Git repository and commit the project for you.
Now create the following directories and files under the memoryLeakApp directory. We will edit them later:
OK, time to start coding. Start by creating LuckyService
, which will generate random numbers and push them to the observable returned by the getLuckyNumber
method. For debugging purposes, we will also implement thegetSubscribersCount
method, which will return the number of clients who subscribed to the observable. Place this code in src/app/lucky/lucky.service.ts
:
Now we have something that we can consume in the component. Place the following code in src/app/lucky/lucky.component.ts
:
Now we are going to create another component. This is necessary to reproduce the memory leak in our app. Place this code into src/app/really/really.component.ts
:
Update NgModule
declaration in the src/app/app.module.ts
:
As the last step, we need to modify src/app/app.component.html
and replace the code generated by the CLI with our routes:
You’ll find all the code up to this point in a GitHub repository, which you can clone:
Time for the Memory Leak
Let’s run the app and see how it’s working:
Navigate to http://localhost:4200 with your browser and you should see:
OK, this looks legit. What we did here is unsubscribe from our observable in the ngOnDestroy
lifecycle hook, which will be executed whenever the component is destroyed, which happens when the page is left by the visitor. Let’s re-run the application and check if the memory leak is gone:
After navigating away from `LuckyComponent`, you should see that the code from subscriptions is no longer executed. The observable has been unsubscribed inside the `ngOnDestroy` hook. Great! Let’s navigate multiple times between components and make sure that everything works as expected, and we are unsubscribing each time.
You’ll find all the code up to this point in this GitHub repository, which you can clone:
TakeUntil Pattern
That worked well for one observable, but what if we have multiple observables, which we need to unsubscribe manually? Do we need to add multiple Subscription
variables, just to unsubscribe them in ngOnDestroy
? Does our code need to look like this:
Of course not. We can do it in a much cleaner way. This is where takeUntil
comes into play. Here’s what the documentation says about the takeUntil
method:
Returns the values from the source observable sequence until the other observable sequence or Promise produces a value.
The most important part of this definition is: until the other observable … produces a value.
OK, so we need that “other observable”. Copy this code into src/app/lucky/lucky.component.ts
:
What we have actually done is declare a new observable:
Then, by using pipe
method with takeUntil
we inform compiler that we want to unsubscribe from the observable when any value appear in onDestroy$
:
Finally, we pushed value to the `onDestroy$` inside the `ngOnDestroy` hook:
Let’s run the app again, and navigate multiple times between components.
That’s it! Observables are unsubscribed, there is no memory leak, and we did all of that with a couple lines of code.
Summary
Today we learned how we could accidentally run into a memory leak in Angular. Then we applied two possible solutions. (I definitely recommend takeUntil
.)
I hope that this post was helpful for you and you will start to use your new knowledge in your one-in-a-million app.
Take a look at the GitHub repository for Step 3 and check out ng-toolkit to learn more Angular and SPA features.
I'm Maciej Treder and you can reach me at contact@maciejtreder.com, https://www.maciejtreder.com and @maciejtreder (GitHub, Twitter, LinkedIn).
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.