Memory Leaks and prevention in Angular

What is a Memory Leak?

Every time you create a subscription to an observable, you're creating a reference to that observable in memory. If weren't 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 a memory leak which can cause a serious problem depending on the size of the applications.

Different ways to handle memory leaks

  1. Unsubscribe using ngOnDestroy
  2. TakeUntil Pattern Using Subjects
  3. Async Pipe

1. Unsubscribe using ngOnDestroy

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();
  }
}

One problem with unsubscribing using the ngOnDestroy() approach is you have to keep a reference to all the observables you're creating in your component.

2. TakeUntil Pattern Using subjects

  • takeUntil is a rxjs operator which takes an observable as an argument.
  • It'll keep the first subscription alive until the second observable finishes.
  • This is far better and easier than the first approach, the observables used takeUntil will be destroyed once the unsubscribe observable is fired.
import {Component, OnDestroy, OnInit } from '@angular/core';
import {Subscription} 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();
  }
}

3. Async Pipe

  • 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.
import {Component, OnDestroy, 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();
  }
}

and in the HTML,

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

Cons of Async Pipe

  • can't reuse a subscription when using an async pipe.
  • forced to use multiple async pipes for same observables which will create multiple subscriptions.

Conclusion

In this post, we have seen the different methodologies to prevent memory leaks. In the upcoming post, we will see a demo of this memory leak.

Did you find this article valuable?

Support Makereading by becoming a sponsor. Any amount is appreciated!