How To Prevent Memory Leaks In Angular Observables

 

You started learning angular and decided to develop a great idea using angular. You’ve been doing very good and making a lot of progress except that your app is not performing well. Now just open your app in the browser, once the page begins to crawl, check the memory usage and it goes to the skies.

 

You keep your app open for some 60 minutes and your laptop battery drains drastically. You have no idea why this happens, you must be following all the best practices to get ride out of it. But, you’re not alone my friend, We’re all been there, done with that. You know what’s the problem is, we’re leaking memory, most probably through the subscriptions.

 

Every time you create a subscription to an observable, you’re creating a reference to that observable in memory, If you’re not being careful and unsubscribe then the reference will live in the memory even if the component you created for the observable has been destroyed. This is called as a memory leak which can cause a serious problem depends on the size of your application.

 

Let’s look into the different ways of handling this problem in Angular and discuss them.

 

 

1) Unsubscribe

Unsubscribing is the simplest solution to this problem, whenever you create a subscription, you have to keep a reference to that subscription and once you’re finished with it, you have to call .unsubscribe on the reference, like below.

 

import {Component, OnDestroy, OnInit} from '@angular/core';
import {Subscription} from 'rxjs';
import {AppService} from './app.service';

@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy{
 title = 'leakage';
 reference: Subscription;
 constructor(private service: AppService) {}
 ngOnInit() {
   this.reference = this.service.getAll()
     .subscribe(res => {
       console.log(res);
     });
 }
 ngOnDestroy() {
   this.reference.unsubscribe();
 }
}

 

READ NOW >>  Angular 6: Experts Revealing Their Favourite Feature & Who Inspired Them

The usage of ngOnDestroy lifecycle hook is important here, it’ll be called when the component is about to be destroyed. One problem with this approach is you have to keep a reference to all the observable you’re creating in your component.

 

Tips: How to make Angular Application SEO Friendly Using Pre Render

 

2) TakeUntil

The previous approach had a problem that we need to create a reference for all the variables and to avoid that problem we can use takeUntil operator to unsubscribe. takeUntil is a rxjs operator which takes an observable as an argument. It’ll keep the first subscription alive until the second observable finishes like below.

 

import {Component, OnDestroy, OnInit} from '@angular/core';
import {Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {AppService} from './app.service';

@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy{
 title = 'leakage';
 private unsubscribe = new Subject();
 constructor(private service: AppService) {}
 ngOnInit() {
   this.service.getAll()
     .pipe(takeUntil(this.unsubscribe))
     .subscribe(res => {
       console.log(res);
     });
 }
 ngOnDestroy() {
   this.unsubscribe.next();
   this.unsubscribe.complete();
 }
}

 

This is far better and easier than the first approach, the observables used takeUntil will be destroyed once the unsubscribe observable fired.

 

Best To Read: Top 10 Angular Blogs You Must Read

 

3) Async Pipe

When can be used and used properly this is the ideal approach than the first two. The async pipe takes an observable as an argument and returns the value from the observable directly to the template. So when a component is destroyed we don’t need to do anything because the async pipe will take care of unsubscribing from the observable. This is the most performant way of subscribing and unsubscribing to observables. Let’s see it in action.

 

import {Component, OnInit} from '@angular/core';
import {Observable} from 'rxjs';
import {AppService} from './app.service';

@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
 title = 'leakage';
 listItems: Observable<any>;
 constructor(private service: AppService) {}
 ngOnInit() {
   this.listItems = this.service.getAll();
 }
}

 

 

In the template,

 

<ul>
 <li *ngFor="let item of listItems | async">{{item.name}}</li>
</ul>

 

There are few caveats like you can’t reuse a subscription when using an async pipe. You are forced to use multiple async pipes for same observable which will create multiple subscriptions but there are different methods to overcome those issues.

 

We’ve seen all the methods to unsubscribe from an observable, no matter which method you choose to work with, always unsubscribe to avoid memory leakage issues. 

 

Love to read more tips and tricks on “Angular”? Get to view the largest blog portal and get seamless tips straight from our Angular experts