Unsubscribing from every infinite observable... a good habit or overkill?
Tags: angular, rxjs, discuss
Published: 2024-04-25 09:47:15

I've seen a lot of discussions[^1][^2][^3] about necessity of unsubscribing from observables during Angular component's destroying. The rule of thumb which I picked up is: when you explicitly `subscribe()` to an _infinite_ observable - you are responsible for unsubscribing from it, otherwise you might end up with a memory leak due to subscriptions, which remain alive after the component is destroyed. 

Of course, it doesn't relate to _finite_ observables, such as `HttpClient` calls, which _complete_ right after the request is finished. Also we are safe with `async` pipes, as Angular does unsubscription for us in this case.

There are various ways of unsubscribing: we can group all subscriptions into one `Subscription` object and then explicitly unsubscribe in `ngOnDestroy` or we can create a `Subject`, which completes on component destroy and use `takeUntil` operator with every subscription. Angular 16 made it even easier by introducing injectable `DestroyRef` and `takeUntilDestroyed` operator[^4].

Nevertheless, is it OK to mindlessly add these safety guards to _every_ subscription?

I realized that in many cases when the target observable is a property (direct or indirect) of the same component - there are no problems with garbage collection.

Take a look at [this stackblitz](https://stackblitz.com/edit/stackblitz-starters-2a3hou?file=src%2Ffoo%2Ffoo.component.ts). For illustrative purposes `FooComponent` reserves a 10MB array which is referenced in the subscription to `valueChanged` observable of reactive form control. We can create and destroy the component several times and eventually see that the component instance, form model, 10MB array and subscription - everything is garbage collected. I used `FinalizationRegistry` to see it even more clearly.

Subscription to URL params from injected `ActivatedRoute` instance? It is cleaned up too. Angular Material paginator/sort as view children of the component? No problem either.

This does not mean that memory leaks are impossible. If your service spawns an infinite observable and you don't unsubscribe in your component - you will have a destroyed component zombie object in the heap. Until the service is destroyed, which is likely equal to 'until you quit the app'.

Things also may get more complicated if we do some processing of observable data in between, there may be a lot of examples.

However, do we need to blindly follow the rule and populate our code with `takeUntil` or whatever just for the sake of clarity? Can we think a little bit deeper and keep the code cleaner where possible? Or is it better to keep the habit and insert safeguards everywhere, just to be sure that we didn't miss a possible problem? What do you think?

---

_Just after I have written this post my friend guided me to [this](https://dev.to/this-is-angular/always-unsubscribe-no-exceptions-debate-closed-205n) publication by Daniel Glejzner, which provides killer arguments in favor of "keeping the habit" even in case of `HttpClient` observables._

[^1]: https://stackoverflow.com/questions/38008334/angular-rxjs-when-should-i-unsubscribe-from-subscription
[^2]: https://stackoverflow.com/questions/70093116/do-i-need-to-unsubscribe-from-an-angular-observable
[^3]: https://stackoverflow.com/questions/61147869/do-i-need-to-unsubscribe-manually-angular-8
[^4]: https://angular.io/api/core/rxjs-interop/takeUntilDestroyed