In Angular: Use Async-Pipe to manage Observable Subscriptions and Prevent Memory Leaks.

In Angular: Use Async-Pipe to manage Observable Subscriptions and Prevent Memory Leaks.

Async-Pipe is an Angular built in tool to manage Observable-Subscriptions. We can easily simplify functionality of an Angular code using the async pipe. Let's learn to use Async-Pipe | Observable-Subscriptions.

Async-Pipe manages Observable-Subscriptions, Observables are the type of variables whose values are being tracked whenever changed - to make sure we always get updated value. We subscribe to an Observable to get its updated value.

We'll learn to use Async-Pipe:


Angular Async-Pipe

The angular async pipe 'allows the subscription to observe value of anything inside of the angular template syntax'. It also takes care of unsubscribing from observables automatically.

#1 TO CREATE –>  IMPLEMENT 'Observable'.

In this example we'll create a component with a very simple observable that increments a value by one in every second and outputs that value.
(Basically this observable is just counting up)
Example-1:
Create a new Component or Open your own component (if you already have one). To create new, run this command👇👇

ng g component YourComponentName

Now, open up your ***.component.ts and update the below code👇👇
(Make sure to replace HomePageComponent with your component name. Similarly the name of selector, templateUrl and styleUrls will be replaced by your name.)

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs'

@Component({
  selector: 'app-home',
  templateUrl: './home.page.component.html',
  styleUrls: ['./home.page.component.css'],
})
// we must 'implements OnInit' to use 'OnInit'
export class HomePageComponent implements OnInit {

  // create an 'Observable', variable name: 'observableNumber'
  observableNumber: Observable<number>;

  ngOnInit() {
    // create an Observable
    this.observableNumber = Observable.create(observer => {
      // initialize value of temp variable 'val' with 0
      let val = 0;
      const interval = setInterval(() => {
        // observer.next will announce the change made in 'val
        // by 'observableNumber'
        observer.next(val);
        // increment value of 'val' after every 1 sec
        val++;
      }, 1000);
      return () => clearInterval(interval);
    });
  }
}
(We must 'implements OnInit' to use 'OnInit'. Learn more –> Click here👆)

#2 TO DISPLAY –>  RESOLVE 'Observable'.

To display the value we'll reference the observable property and use the async pipe to resolve the observable:

Now, open up your ***.component.html and add this code👇👇

<p>{{ observableNumber | async }}</p>
Learn more about Interpolation syntax {{ }} –> Click here👆)

The output of 'observableNumber' will display the count value on your screen - increment in every second.


#3 RESOLVE 'Observable' - without using 'Async-Pipe'.

A very common use case of 'Observable' is to display values received from a REST-Endpoint/APIs because the Angular HttpClient returns an observable.
Here we will learn to 'Resolve Observable' returns from 'REST-Endpoint/APIs'.

Why we need to use the async pipe?
There are many ways we can subscribe to observables. The default way (without angular) is to subscribe the observable manually and update a separate property with the value, check example below: 👇👇
Example-2:
Open up your ***.component.ts and update the below code👇👇

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs'

@Component({
  selector: 'app-home',
  templateUrl: './home.page.component.html',
  styleUrls: ['./home.page.component.css'],
})
// we must 'implements OnInit' to use 'OnInit'
export class HomePageComponent implements OnInit {

  // create an 'Observable', variable name: 'observableNumber'
  observableNumber: Observable<number>;
  // create another variable 'latestValue'
  // this variable will store the most updated value
  latestValue: number;

  ngOnInit() {
    // create an Observable
    this.observableNumber = Observable.create(observer => {
      // THIS IS THE SAME CODE WE USED IN PREVIOUS EXAMPLE-1 TO 'create an Observable'
      
      // initialize value of temp variable 'val' with 0
      let val = 0;
      const interval = setInterval(() => {
        // observer.next will announce the change made in 'val
        // by 'observableNumber'
        observer.next(val);
        // increment value of 'val' after every 1 sec
        val++;
      }, 1000);
      return () => clearInterval(interval);
    });
    
    // THIS IS THE NEW CODE
    // subscribe to an Observable
    // make sure to save a reference to subscription to
    // be able to unsubscribe later
    this.observableNumber.subscribe(value => {
      // this subscription make sure to have latest value always
      this.latestValue = value;
    });
    
    ...
  }
}

We can now bind to the property without using the async pipe:
(instead of using 'observableNumber' we are using 'latestValue' to get updated value because we have already 'subscribed observableNumber' in the 'ngOnInit' function to resolve 'Observable')👇👇

Now, open up your ***.component.html and add this code👇👇

<p>{{ latestValue }}</p>

The output of 'latestValue' will display the count value on your screen - increment in every second – same as we see in previous example-1.


So why would we use the async-pipe then?

In Example-2 we subscribed to the observable manually, we also need to manually unsubscribe. Otherwise, we risk a memory leak when the component is destroyed.

To fix this, 'we need to unsubscribe when the component is destroyed'. The best place to do that is the 'ngOnDestroy' lifecycle hook:

Open up your ***.component.ts and update the below code👇👇

// import 'OnInit' and 'OnDestroy'
import { Component, OnInit, OnDestroy } from '@angular/core';
// also import 'Subscription'
import { Observable, Subscription } from 'rxjs';

@Component({
  selector: 'app-home',
  templateUrl: './home.page.component.html',
  styleUrls: ['./home.page.component.css'],
})
// we must implements 'OnInit' and 'OnDestroy'
export class HomePageComponent implements OnInit, OnDestroy {

  ...
  // create a variable to store subscription
  subscriptionToObservableNumber: Subscription;

  ngOnInit() {
    // create an Observable
    this.observableNumber = Observable.create(observer => {
      ...
    });
    
    // subscribe to an Observable
    // make sure to save a reference to subscription to
    // be able to unsubscribe later
    this.subscriptionToObservableNumber = this.observableNumber.subscribe(value => {
      // this subscription make sure to have latest value always
      this.latestValue = value;
    });
  }
  
  // ngOnDestroy executes when component is destroyed
  ngOnDestroy() {
    // unsubscribe when the component is destroyed
    this.subscriptionToObservableNumber.unsubscribe();
  }
  
}

A cleaner and more reactive way of doing the same thing is to use the 'rxjs takeUntil' operator with another observable/subject that we complete when the component is destroyed. In this case, the 'takeUntil' operator is taking care of unsubscribing.

Open up your ***.component.ts and update the below code👇👇

// import 'OnInit' and 'OnDestroy'
import { Component, OnInit, OnDestroy } from '@angular/core';
// also import 'Subscription'
import { Observable, Subject, Subscription } from 'rxjs';
import {takeUntil} from 'rxjs/operators';

@Component({
  selector: 'app-home',
  templateUrl: './home.page.component.html',
  styleUrls: ['./home.page.component.css'],
})
// we must implements 'OnInit' and 'OnDestroy'
export class HomePageComponent implements OnInit, OnDestroy {

  // create an 'Observable', variable name: 'observableNumber'
  observableNumber$: Observable<number>;
  // create another variable 'latestValue'
  // this variable will store the most updated value
  latestValue: number;
  // create a variable to store subscription
  subscriptionToObservableNumber: Subscription;
  // declare unsubscribe$
  unsubscribe$: Subject<void> = new Subject<void>();

  ngOnInit() {
    // create an Observable
    this.observableNumber$ = Observable.create(observer => {
      ...
    });
    
    // the 'takeUntil' operator is taking care of unsubscribing
    this.observableNumber$
    .pipe(takeUntil(this.unsubscribe$))
    .subscribe(value => {
      this.latestValue = value;
    });
    
    // subscribe to an Observable
    // make sure to save a reference to subscription to
    // be able to unsubscribe later
    this.subscriptionToObservableNumber = this.observableNumber$.subscribe(value => {
      // this subscription make sure to have latest value always
      this.latestValue = value;
    });
    
    // create an Observable
    this.observableNumber$ = Observable.create(observer => {
      ...
    });
  }
  
  // ngOnDestroy executes when component is destroyed
  ngOnDestroy() {
    // unsubscribe when the component is destroyed
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
  
}
  • This approach is especially useful when dealing with multiple observables per subscription, as we do not need to keep a list with all subscriptions.
  • After all, this additional syntax is not necessary when using the angular async pipe, as the pipe itself takes care of unsubscribing from the observable once the component is destroyed. So, if nothing else, the async pipe makes our code cleaner.
  • Also, the methods showed above do not work with the onPush change detection strategy, which is used to do performance optimizations of components. Async pipe, on the other hand works just fine with that.

That is why "we should definitely use the async-pipe wherever and whenever possible".


#4 ASYNC-PIPE with *ngIf and *ngFor

With *ngIf
Interpolation {{ }} is not the only data binding to which the async pipe can be used. We can also use it with the *ngIf directive:

Now, open up your ***.component.html and try this code👇👇

<p *ngIf="(observableNumber$ | async) > 5">{{ observableNumber$ | async }}</p>
Note, that the braces inside the *ngIf directive are absolutely necessary in this case.

The above <p> will only be visible when the value of 'observableNumber$' becomes greater than 5.

With *ngFor
We can use the async pipe for *ngFor the same way we use for *ngIf directive.
But to use the async-pipe we must need the observable of type array, not just a single value. See this👇👇

...

@Component({
  ...
})
// we must implements 'OnInit' and 'OnDestroy'
export class HomePageComponent implements OnInit, OnDestroy {
    ...
    items$: Observable<number[]>;

    constructor() {}
}

Now, we can use this in *ngFor directive like this👇👇

<p *ngFor="let item of items$ | async">{{ item }}</p>

Conclusion

We can use Angular async-pipe to prevent memory leaks.
The things we covered are:
#1 TO CREATE –> IMPLEMENT 'Observable'.
#2 TO DISPLAY –> RESOLVE 'Observable'.
#3 RESOLVE 'Observable' - without using 'Async-Pipe'.
#4 ASYNC-PIPE with *ngIf and *ngFor.


Done! 🤩 It’s that simple to understand 'why to use Async-Pipe to manage Observable-Subscriptions'.

See you later 👋👋


Feel free to comment down in the comment box… If I missed anything or anything is incorrect or anything does not works for you :)
Stay connected for more articles.

Next, Learn to Use 'Promises' | Async/Await | in place of JavaScript Callbacks.

Use ‘Promises’ | Async/Await | in place of JavaScript Callbacks.
We should use Promises which allows us to access asynchronous method and returnvalues to synchronous methods. and Async/Await is the extension of promises. We are here to.. * Understand what `Synchronous` and `Asynchronous`. * Understand `Callback` - with an Example. * Understand `Promises` - w…
Click here ↑ to Read