Unsubscribing in Angular, the right way
By: Ashwin Sathian
Tags: Asynchronous, Subscription, Rxjs, Angular

Once you get a hold of it, event-driven implementations using RxJS go a long way in making your Angular app dynamic. Even if you’re in your early days of using Angular, it’s highly recommended that you make use of Observables, Subjects, Subscriptions, etc. in your application to get the best out of the app. That said, if not done right, RxJS implementation can come back to you haunt you in the form of performance drops for your applications. And one of the top things even developers who have been into Angular for a while may get wrong is the concept of unsubscribing and how best to do it. That’s what we’ll be exploring in this post.

Is unsubscribing necessary in the first place?

Absolutely. Granted, your observables and subscriptions will work just fine even if you don’t with the whole unsubscribing business. To the untrained eye, particularly in a small-ish application, everything may look good. Let me try and explain with a real-life analogy what unsubscribing can entail.

Consider that you’re at a restaurant. Before the meal comes in, you decide to wash your hands. You go to the washroom, turn the tap on, and press the soap dispenser. For some reason, the soap dispenser stays in the pressed down position and soap keeps dripping out. You wash your hands, and decide to leave the tap open — what’s the harm, someone will eventually come and close the tap and also fix the soap dispenser. Minutes later, when you’re enjoying your meal, or even an hour later when you’ve finished your filet mignon, left a handsome tip and stepped out, the tap in the washroom is still letting water out and so is the unchecked soap dispenser. See where I am going with this?

Although you never saw it, a lot of water was lost, and so was a lot of soap. There’s even a fair chance that the drain may get clogged up leading to a messy situation. This is pretty much what’d happen if you leave your observables open and unsubscribed after use. Unchecked memory leaks will happen during application runtime and may lead to issues although on the outside, at least for the initial part at least, you may be blissfully ignorant of the trouble to come.

To see this in action, subscribe to an observable in a component and keep logging the data from the observable. You’ll see that the logging will carry on.

The component will get destroyed but the subscription will live on

More logs will keep getting added to the browser console. More so, if we navigated back to the original route. The component would get recreated together with a new subscription. That’s why it’s absolutely critical that after use, observables be unsubscribed from, to relieve the subscription the application has placed on it to track some update.

Angular, being the robust framework it is, internally takes carte of unsubscribing from implicitly created observables such as HTTP calls, async pipes, etc. The Observables and Subscriptions which we create however, we have to unsubscribe from them explicitly.

With that out of the way, let’s looks ta various ways to unsubscribe from an observable(yes, there’s more than one way).

Method 1: The Manual Way using unsubscribe()

This is the most commonly used method, the one that we teach beginners getting started on Observable and Subscriptions.

Not much rocket science is happening here, as can be seen. All we do in this method is calling unsubscribe on the Subscription if it’s defined, as and when the component is destroyed. Simple, and gets the job done for the most part.

Now imagine that your component has many such subscriptions, it can quickly become quite a process to ensure everything gets unsubscribed when the component is destroyed, not to mention the fact that the ngOnDestroy function will be a lot more lines longer. This is where we need a better approach and that’s where the next method comes in.

Method 2: Declarative or ‘automatic’ using takeUntil()

According to the official docs,

takeUntil(notifier: Observable<any>) — Emits the values emitted by the source Observable until a notifier Observable emits a value.

In simple words, we can use the takeUntil operator to make subscriptions smarter by passing a boolean-typeobservable to it. Once we follow this method, all we ned to do in the OnDestroy method is make the observable truthy and unsubscribe from it alone, which will, in turn, stop all the subscriptions handled in this manner. Check out the following example implementation.

Is this method the silver bullet for the whole unsubscribe issue? Not quite.

As much as it’s effective, it has downsides and gotchas of its own.

It’s verbose, what with the need to declare a Subject to use as a notifier for the takeUntil method There are a fair few things that you can mess up, such as but not limited to forgetting to make either or both of the next() and unsubscribe()(or complete()) calls on our notifier subject, or forgetting to implement the OnDestroy method itself, to name a couple. Special care has to be taken to ensure that in case you’re using some other operators like map(), filter(), etc. inside pipe(), takeUntil should be placed last so that all generated observables are handled.

Method 3: There’s always the prodigal async pipe

Pipes are one of the great things about Angular. As much as custom pipes can be written, the pipes we get out of the box itself are plentiful and one such incredible pipe is the async pipe. This allows you to use observables directly in the template, as shown below

That this is the most compact solution yet, is a matter-of-fact beyond doubt. And look, no hassles of takeUntil, subscriptions, OnDestroy method, nothing. And yet, this has downsides, a big one in particular.

Since data is resolved directly in the template, no manipulation can be done on it from the component, but will have to be used as is.

In conclusion, this is like one of those math problems. There’s no unique method. It all boils down to situation and personal choice, which you develop over time of writing Angular code. That said, takeUntil and async pipe approaches stand shoulder to shoulder, and leaps and bounds ahead on simple unsubscribe(Method 1). Personally, I use the takeUntil method because I like to have the data available in the component just in case.