From my understanding of Angular and RxJs there are two ways to terminate Observables. You can unsubscribe()
from them or use takeUntil()
and complete()
. Below are examples of each approach (in pseudocode).
The unsubscribe() approach
private _id: number;
private _subscriptions: Subscription[] = [];
constructor(private _route: ActivatedRoute) {
this._getId();
}
public ngOnDestroy(): void {
this._subscriptions.forEach(
subscription => subscription.unsubscribe()
);
}
private _getId(): void {
this._subscriptions.push(
this._route.params.subscribe(params => this._id = +params['id'])
);
}
The takeUntil() and complete() approach
private _id: number;
private _ngUnsubscribe: Subject<void> = new Subject<void>();
constructor(private _route: ActivatedRoute) {
this._getId();
}
public ngOnDestroy(): void {
this._ngUnsubscribe.next();
this._ngUnsubscribe.complete();
}
private _getId(): void {
this._route.params.takeUntil(this._ngUnsubscribe).subscribe(
params => this._id = +params['id']
);
}
In Angular, is there a preferred way to terminate Observables?
Both approaches are correct even though they aren't equivalent.
In my opinion (and experience) using unsubscribe()
make usually more sense and is more obvious to other developers that don't have extensive experience with RxJS.
Using takeUntil()
is recommended by the lead developer of RxJS 5 (https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87) and is sometimes easier to use than handling multiple subscription objects. For example if you use the partition()
operator to split one stream into two it's easier to use just takeUntil(...).partition(...)
.
However there are two important things:
These two aren't the same. If you use takeUntil()
you're completing the Observable chain which means that all complete handles are called followed by tear down functions. On the other hand when you call unsubscribe()
only tear down functions are called (including operators such as finally()
).
This is why I think it makes more sense to use unsubscribe()
. With takeUntil()
you might have a complete
handler that is invoked even though you just wanted to unsubscribe (not mentioning that this triggers operators that work with the complete
signal such as repeat()
that might resubscribe again). The fact you want to unsubscribe doesn't mean that the source Observable completed. You just don't care any more about its values so it's probably better to use unsubscribe()
in this case.
However, in practise it usually doesn't matter whether you complete the chain or just unsubscribe.
You can compose Subscription
s into a single one and unsubscribe all of them at once:
const subscription = new Subscription();
const sub1 = Observable...subscribe(...);
const sub2 = Observable...subscribe(...);
const sub3 = Observable...subscribe(...);
subscription.add(sub1).add(sub2).add(sub3);
...
subscription.unsubscribe(); // unsubscribes all of them
The way that I do this is by first checking on ngOnDestroy
, if the Observable exists. If yes, then unsubscribe from it. Following is the code snippet from my app.
accountSubscriptionObj : Subscription;
ngOnDestroy() {
if (this.accountSubscriptionObj) {
this.accountSubscriptionObj.unsubscribe();
}
}
Meanwhile, you don't need to unsubscribe from Async pipe, @HostListener,
Finite Observable.
When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks. When you have a finite sequence, usually you don’t need to unsubscribe, for example when using the HTTP service or the timer observable. For more reference read here.