RXJS - Angular - unsubscribe from Subjects

didgewind picture didgewind · May 31, 2018 · Viewed 16.3k times · Source

As described in this thread, 'official' solution to unsubscribe from Observables in Angular 5+ in general is using takeUntil. So far, so good. My question is, does this also apply if the Observable I am subscribed to is actually a Subject?

Answer

Per Hornshøj-Schierbeck picture Per Hornshøj-Schierbeck · Jun 1, 2018

Once you call .subscribe() on anything (Subjects too), something needs to make sure the subscription gets unsubscribed.

Dealing with finite Observables: If you subscribe to a finite observable (meaning an observable that has a finite/limited sequence), the last message will send an end signal and the subscription will be canceled automatically. Examples of this are:

Observable.of(100)
Observable.from([1,2,3,4])

Examples of infinite observables are:

Observable.fromEvent(document, 'click')
Observable.timer(1000)

Calling/piping .first(), .take(number) or .takeWhile(condition that will evaluate to false at some point) or takeUntil(observable that emits a value) on an observable will all turn an otherwise infinite observable into a finite one.

Stop calling .subscribe(): Another popular method of not having to unsubscribe is by not subscribing in the first place. This might sound stupid, since when would you want an observable that you do not subscribe to? Well if you only need to pass some data to your view/html template, piping that observable into the async pipe will pass the unsubscribing issue to the async pipe itself.

Typical examples in the html template:

<h1>Editing {{ infiniteObservable$ | async }}<h1>
<li *ngFor="let user of userObservable$ | async as users; index as i; first as isFirst">
   {{i}}/{{users.length}}. {{user}} <span *ngIf="isFirst">default</span>
</li>

Manually unsubscribing: Lastly, you can choose to keep references to all subscriptions. You don't have to keep a variable pointing to each subscription, it's easier to just use a single Subscription object to keep track of all the subscriptions, and then unsubscribe to all of them at once. Here is an example:

const subscriptions = new Subscription();
subscriptions.add(observable1$.subscribe());
subscriptions.add(observable2$.subscribe());
subscriptions.unsubscribe();

Quick summerize, how to handle unsubscriptions, any of the below methods:

  1. Turn infinite observables into finite ones, hereby removing the need to unsubscribe (use .takeUntil(this.destroyed$) and do this.destroyed$.emit() in ngOnDestroy()).
  2. Avoid subscribing, and passing the observable though the async pipe.
  3. Keep a reference to any subscriptions and call .unsubscribe() in the ngOnDestroy() method.

Personally i tend to only use one of the two first methods.