Angular: Avoid the Subscribe() Method for Observables

Use AsyncPipe instead

Outdated tech products on a desk.
Outdated tech products on a desk.
Photo by Lorenzo Herrera on Unsplash.

One of the best-practice principles of Angular is to always use AsyncPipe when possible and only use .subscribe() when the side effect is an absolute necessity and cannot be avoided.

What Is an Observable?

An Observable is an abstraction of an asynchronous stream of data. For example, when we look at anObservable, it represents a stream of strings that will be delivered one by one over time. TK (https://medium.com/isop-nepal/subscribe-vs-async-pipe-in-angular-21bb38f3ee49)

But when the Observable gets modified, how do we display data? This is where we use .subscribe().

Subscribe Function

We pass the Observable around, combining it and saving it to different variables with different combinations of operators, but at the end, an Observable<T> is useless on its own. We need a way to “terminate” the Observable and extract the type T out of it. That is what .subscribe is used for: to subscribe to the resulting stream and terminate the observable. TK (https://kimsereyblog.blogspot.com/2018/05/async-pipe-versus-subscribe-in-angular.html)

We all recognize this pattern where I dispose of a subscription with the unsubscribe() method:

With this pattern, I can be sure that my subscription will always be ended and I am safe from a memory leak! Note that we absolutely have to create the subscription during the initialization (ngOnInit) of a component, and later when it’s being destroyed, we need to check if the subscription is still live, cancel it, and clear all references to it. TK (https://medium.com/angular-in-depth/why-you-have-to-unsubscribe-from-observable-92502d5639d0)

But for most regular situations where I am just doing a simple subscription to get the value of a variable and passing it to the template, this pattern becomes tedious very fast with an increasing number of subscriptions.

For example, see the code below:

So here is how I can avoid the subscription completely.

But first, let’s look at the code where it’s a simple consumption of an Observable returned by a service function and how to handle this with the .subscribe() method

Let’s assume I have a serviceFunctionReturningObservable() function that returns me an observable of the following signature. And in the parent component.ts file, I want to subscribe to that Observable and pass that subscribed data down to Children to be consumed.

serviceFunctionReturningObservable( flag: Flag ):
Observable<boolean> {}

The suboptimal version of my code (without using an async pipe) with regular .subscribe()

And then in the corresponding .html template file, I access the data as follows:

[isSomeBooleanVarToPassDownToChildComp]="isSomeBooleanVarToPassDownToChildComp"

Now to understand what’s happening above, we need to note that normally to render the result of a Promise or an Observable, we have to:

  1. Wait for a callback.
  2. Store the result of the callback in a variable.
  3. Bind to that variable in the template. TK (https://codecraft.tv/courses/angular/pipes/async-pipe/)

So going by that flow, what we are doing above is:

  • Creating a boolean Observable that publishes out a boolean value within the ngOnInit().
  • Rendering the value of isSomeBooleanVarToPassDownToChildComp in our template.
  • Subscribing to the output of this Observable chain and storing the boolean on the property isSomeBooleanVarToPassDownToChildComp.

AsyncPipe

But with AsyncPipe, we can use Promises and Observables directly in our template without having to store the result on an intermediate property or variable. TK (https://codecraft.tv/courses/angular/pipes/async-pipe/)

AsyncPipe accepts as argument an Observable or a Promise, calls subscribe or attaches a then-handler, then waits for the asynchronous result before passing it through to the caller. TK (https://codecraft.tv/courses/angular/pipes/async-pipe/)

So for this case, we can do even better and never actually use subscribe by using AsyncPipe.

“Unwraps a value from an asynchronous primitive.

The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted. When a new value is emitted, the async pipe marks the component to be checked for changes. When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks.” — Angular’s documentation

Why Use AsyncPipe?

  • Because it automatically subscribes and unsubscribes from Observables as the component gets instantiated or destroyed, which is a great feature.
  • This is especially important in the case of long-lived Observables (e.g. certain Observables returned by the router or by AngularFire).
  • Also, because it makes our programs easier to read and more declarative with fewer state variables in our component classes.

By using AsyncPipe, we don’t need to perform the subscribe and store any intermediate data on our component like so:

In case I had to pipe something with the initial subscribed data, I would have to do this:

We pipe our Observable directly to the AsyncPipe, it performs a subscription for us, and then it returns whatever gets passed to it. TK (https://codecraft.tv/courses/angular/pipes/async-pipe/)

By using AsyncPipe, we:

  • Don’t need to call subscribe on our Observable and store the intermediate data on our component.
  • Don’t need to remember to unsubscribe from the Observable when the component is destroyed.
  • Have fewer state variables. TK (https://codecraft.tv/courses/angular/pipes/async-pipe/)

Another Example of the Principle

In the case below, arrayListFromSelector$ is coming from a selector (using the reselect package and consuming a redux-reducer state).

Initially, I was subscribing to that selector coming from reselect/redux with .subscribe() as follows.

Initial working code with .subscribe()

And then passing that subscribed data arrayListFromSelector down to the Child component where I will, in turn, just pass that data, as it is ultimately to be consumed by an ng-select to be fed to a dropdown list.

However, your Child component does not need to know anything about the Observable.

Final refactored code without using .subscribe()

And now arrayListFromSelector is available in the Child, just as a local state variable.

Generally, the pattern of “subscribe and, in the subscription function, copy data into the state of the component” is not a healthy one. It gets you unnecessarily involved in change detection.

Exceptions

But there are some cases where you should explicitly subscribe to Observables in components and directives.

For example, when the Observable makes a change to the outside world. Most commonly, this is done by issuing a POST (or DELETE or PUT) through HTTP to a back-end API.

When working with HttpClient, we might face a situation where we just can’t use AsyncPipe for the Observable. To dive deeper, let’s look at some examples:

  • Re-sending requests when parameters are being changed.
  • Receiving data in parallel from several streams.
  • Sending individual requests.
  • Creating, deleting, and updating data.

Also, when the component itself — and not Angular via a template — is consuming the data. It frequently happens when a component opens a modal dialog or sends a message to the user, like a pop-up or a “snackbar.”

In all those cases, the component is the entity that “wants” and “consumes” the Observable to actually execute, so it also should be the one to subscribe.

But otherwise, whenever possible:

“We should always use AsyncPipe when possible and only use .subscribe when the side effect is an absolute necessity, as we are safe as long as we stay in the Observable. The code terminating the Observable should be the framework (Angular) and the last piece (the UI).” TK (https://kimsereyblog.blogspot.com/2018/05/async-pipe-versus-subscribe-in-angular.html)

Written by

MachineLearning | DataScience | DeepLearning. Previously Frontend Engineer. Ex International Banking Analyst. https://www.linkedin.com/in/rohan-paul-b27285129/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store