Angular - what is the preferred way to terminate O

2019-04-06 23:42发布

问题:

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?

回答1:

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:

  1. 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.

  2. You can compose Subscriptions 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
    


回答2:

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.