Asynchronous programming
Angular Best Practices HTML Observables RxJS SPA Typescript Visual Studio Code

Using Asynchronous Observables and Subscriptions in an Angular Application

Welcome to today’s post.

In today’s post I will be discussing the use of subscriptions and asynchronous observables within an Angular application. I will also explain the reasons for using either of these resources during development.

I will first define what subscriptions and asynchronous observables are. I will then give an overview of the pros and cons of each method, and then give an example of how each is used most effectively.

What are Subscriptions?

A subscription is used to perform an execution on an Observable. The subscription object is a disposable resource that should be released after execution. A common Observable is an http client request to a web API service. A subscription executes asynchronously within the current application thread.

The results from an executed observable are handled by an anonymous lambda function block. Below is an example of how this is done:

import { of } from 'rxjs';/

…

const myObservable = of(1,2,3,4,5);
const mySubscription = myObservable.subscribe(res => 
{
    Console.log(“result is : ” + res;
});

To dispose of the above subscription, we use the unsubscribe() method of the subscription object as shown:

ngOnDestroy()
{
    this.mySubscription.unsubscribe();
}

In the next section, I will define asynchronous observations.

What are Asynchronous Observables?

An asynchronous observable, which is also known as an asynchronous pipe is (like a subscription) used to perform an execution on an Observable. In this case, the creation and disposal of the subscription is handled automatically by Angular.  

The subscription is executed within a HTML template.

An example of an asynchronous observable is below:

import { of } from 'rxjs';

…

const myObservable = of(1,2,3,4,5);

And the HTML template is:

<li *ngFor="let data of myObservable$ | async">
    Item Value={{ data }}
</li>

In the above template, the async pipe is the excerpt:

myObservable$ | async

In the next section, I cover some advantages and disadvantages of both approaches.

Advantages and disadvantages of both methods

Some of these advantages and disadvantages that I will be covering are determined by the type of the application or component that we are using. A user interface component and a service component both have different reasons for favoring one type of resource over another.

Having used both types of observable subscriptions within components and these are some advantages and disadvantages of their respective uses:

Reasons for using a manually subscribed observable:

1. To control subscribing and unsubscribing subscriptions manually.

2. Synchronizing the loading and manipulation of data within a component before internal use.

3. When the subscribed data is used internally (non-visually) within the component, such as a service or computations.

Reasons for using an asynchronous observable pipe:

1. Subscribing and unsubscribing of subscriptions of observables is handled automatically.

2. Synchronizing the loading and manipulation of data within a component before use within the HTML template.

3. When there are several HTML elements that depend on subscribed data, and you would like the subscriptions released automatically after the component is destroyed.

In both cases you can load and manipulate subscribed data within your component before usage. With the asynchronous observable, the clean-up of observables is handled automatically to prevent memory-leaks within your running application. The use of subscriptions is warranted when there is a need to process data and associated subscriptions synchronously.

A better example of each approach being applied for the same component is shown below:

Manual Subscription approach

TypeScript

import { map, of } from 'rxjs';
…

  someData: SomeClass[] = [
    { id: 1, desc: 'One', data: 100 },
    { id: 2, desc: 'Two', data: 200 },
    { id: 3, desc: 'Three', data: 300 }
  ];

  someData$: any;

  moreData: SomeClass[] = [];
…

  ngOnInit() {
    this.someData$ = of(this.someData).subscribe((res) => {
      this.moreData = res;
    });
  } 

  ngOnDestroy()
  {
    this.someData$.unsubscribe();
  }
}

export class SomeClass {
  id: number;
  desc: string;
  data: number;
}

HTML

<li *ngFor="let data of someData$ | async">
  Item={{ data.desc }}. Value={{ data.data }}
</li>

Asynchronous Observable Pipe approach

TypeScript

import { map, of } from 'rxjs';
…

  someData: SomeClass[] = [
    { id: 1, desc: 'One', data: 100 },
    { id: 2, desc: 'Two', data: 200 },
    { id: 3, desc: 'Three', data: 300 }
  ];

  someData$: any;

  ngOnInit()
  {
    this.someData$ = of(this.someData).pipe(
      map((res) => {
        res.map((r) => {
          r.data = Math.floor(r.data * 1.1);
        });
        return res;
      })
    );
  }

HTML

<li *ngFor="let data of someData$ | async">
  Item={{ data.desc }}. Value={{ data.data }}
</li>

We can ask the question: Is it better to manually subscribe when calculations or pre-processing are used within the component? Not necessarily so.

To conclude, the usage of either option depends on the complexity of your component, it’s type (a visual or non-visual component) and how you would like to manage the memory management of subscriptions.

One argument for sticking to manually subscribed observables is that we can obtain the data first, manipulate it then display it like this:

this.someData$ = of(this.someData).subscribe((res) => {
  console.log('data in array has changed!');
  this.moreData = res.map((r) => {
    r.data = Math.floor(r.data * 1.1);
    return r;
  });
});

We can also achieve the above using asynchronous pipes utilizing the pipe(map(.. RxJS operators to shape the observable data stream before it is subscribed to:

this.someData$ = of(this.someData).pipe(
  map((res) => {
    res.map((r) => {
      r.data = Math.floor(r.data * 1.1);
    });
    return res;
  })
);

As we can see, making the choice depends on the type and visibility of your component, and the number of subscriptions that need management within your component. For visual components with an HTML template, the use of asynchronous observables can make our code cleaner. Non-visual front-end services can use manual subscriptions to receive and transform data before providing it to other application components. It is very much application context driven.

That is all for today’s post.

I hope you have found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial