Angular RxJS Observable: takeUntil vs. unsubscribe

2020-03-08 06:27发布

There are several ways to unsubscribe from observables on Angular components (by using ngOnDestroy). Which option below should be preferred and why (e.g. technical reasons, performance, etc.)?

Option 1: takeUntil

Using RxJS takeUntil to unsubscribe

@Component({
  selector: "app-flights",
  templateUrl: "./flights.component.html"
})
export class FlightsComponent implements OnDestroy, OnInit {
  private readonly destroy$ = new Subject();

  public flights: FlightModel[];

  constructor(private readonly flightService: FlightService) {}

  ngOnInit() {
    this.flightService
      .getAll()
      .pipe(takeUntil(this.destroy$))
      .subscribe(flights => (this.flights = flights));
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

Option 2: .unsubscribe()

Explict call .unsubscribe(), e.g. by using a separate subscription instance

@Component({
  selector: "app-flights",
  templateUrl: "./flights.component.html"
})
export class FlightsComponent implements OnDestroy, OnInit {
  private readonly subscriptions = new Subscription();

  public flights: FlightModel[];

  constructor(private readonly flightService: FlightService) {}

  ngOnInit() {
    const subscription = this.flightService
      .getAll()
      .subscribe(flights => (this.flights = flights));

    this.subscriptions.add(subscription);
  }

  ngOnDestroy() {
    this.subscriptions.unsubscribe();
  }
}

Option 3: takeWhile

Using RxJS takeWhile unsubscribe

Option n: ?

3条回答
够拽才男人
2楼-- · 2020-03-08 06:31
  • Option 1: clean & explicit. Works like a charm
  • Option 2: more procedural, less stream-like. Works like a charm. Note that your stream will not get a 'complete' event which can cause unexpected behaviour
  • Option 3: takeWhile - will have the subscription stay around untill an emission is created and then the takeWhile is evaluated. This can lead to unexpected behaviour. Still, in the end works

TLDR; there is no wrong here. Choose what you see fits your needs and communicates your intent.

Ben Lesh has also written quite a good post about the different ways to unsubscribe https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87

His opinion:

You should probably be using operators like takeUntil to manage your RxJS subscriptions. As a rule of thumb, if you see two or more subscriptions being managed in a single component, you should wonder if you could be composing those better.

查看更多
时光不老,我们不散
3楼-- · 2020-03-08 06:37

Personally, I choose option 1 as well, which is to use takeUntil.

However, it is important for you to place the takeUntil() operator on the last RxJS operator on the pipe sequence, as explained here.

For instance, if we do not place takeUntil() as the last operator, the subscriber will remain subscribed to the subsequent switchMap() operator:

this.flightService.getAll()
  .pipe(
    takeUntil(this.destroy$),
    switchMap(() => this.doSomethingElse()),
  ).subscribe(flights => (this.flights = flights));

Therefore, the right way of doing so would be this:

this.flightService.getAll()
  .pipe(
    switchMap(() => this.doSomethingElse()),
    takeUntil(this.destroy$),
  ).subscribe(flights => (this.flights = flights));

Brian Love has written a really good article on the different ways (including unsubscribe(), and the takeUntil() operator) of unsubscribing to observables. I would recommend you to take a look at it as well.

查看更多
对你真心纯属浪费
4楼-- · 2020-03-08 06:56

For your example I wouldn't choose any of the 3 options. When you only want to assign a value, then store the Observable and subscribe to it with async pipe.

@Component({
  selector: "app-flights",
  templateUrl: "./flights.component.html"
})
export class FlightsComponent implements OnDestroy, OnInit {  
  public flights$: Observable<FlightModel[]>;

  constructor(private readonly flightService: FlightService) {}

  ngOnInit() {
    this.flights$ = this.flightService.getAll();
  }
}

<ng-container *ngIf="flights$ | async as flights">
  <ng-container *ngFor="let flight of flights">
    {{ flight | json }}
  </ng-container>
</ng-container>
查看更多
登录 后发表回答