Angular RxJS Observable: takeUntil против отписки с подпиской

Есть несколько способов отказаться от подписки на наблюдаемые компоненты Angular (с помощью ngOnDestroy). Какой из перечисленных ниже вариантов следует предпочесть и почему (например, по техническим причинам, характеристикам и т. Д.)?

Вариант 1: takeUntil

Использование RxJS takeUntil для отказа от подписки

@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();
  }
}

Вариант 2: .unsubscribe ()

Явный вызов .unsubscribe (), например с помощью отдельного экземпляра подписки

@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();
  }
}

Вариант 3: взять

Использование RxJS takeWhile при отказе от подписки

Вариант n:?


person Horace P. Greeley    schedule 17.10.2019    source источник
comment
Вы можете спросить десять человек, и у всех будут свои предпочтения. Все упомянутые вами варианты абсолютно верны, и нет четкого «лучшего». IMO takeUntil - лучшее решение.   -  person Giovani Vercauteren    schedule 17.10.2019
comment
хорошо, мне было интересно, есть ли технические причины для тех или иных вариантов ... Спасибо за ваш комментарий   -  person Horace P. Greeley    schedule 17.10.2019
comment
@ HoraceP.Greeley Я знаю, что уже поздно, но зачем нужен this.destroy $ .next (); по Варианту 1?   -  person raberana    schedule 24.02.2021
comment
@raberana next () требуется из-за того, что takeUntil проходит до тех пор, пока destroy $ не получит значение путем вызова destory $ .next (). Еще одна тема, но важная: takeUntil должен быть на последней позиции в конвейере. См. cartant.medium.com/rxjs-avoiding-takeuntil-leaks-fb5182d047ef   -  person Horace P. Greeley    schedule 26.02.2021


Ответы (3)


  • Вариант 1: чистый и ясный. Работает как шарм
  • Вариант 2: более процедурный, менее потоковый. Работает как шарм. Обратите внимание, что ваш поток не получит «завершенное» событие, которое может вызвать неожиданное поведение.
  • Вариант 3: takeWhile - подписка будет действовать до тех пор, пока не будет создана эмиссия, а затем будет оцениваться takeWhile. Это может привести к неожиданному поведению. Тем не менее, в конце концов, работает

TL; DR; здесь нет ничего плохого. Выберите то, что вы видите, соответствует вашим потребностям и выражает ваши намерения.

Бен Леш также написал неплохой пост о различных способах отказа от подписки https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87.

Его мнение:

Вероятно, вам следует использовать такие операторы, как takeUntil, для управления подписками RxJS. Как показывает опыт, если вы видите, что двумя или более подписками управляют в одном компоненте, вы должны задаться вопросом, могли бы вы составить их лучше.

person Mark van Straten    schedule 17.10.2019
comment
Должны ли мы использовать this.destroy$.next() и this.destroy$.complete() в ngOnDestroy() при использовании takeUntil? - person Fredrick; 26.12.2020
comment
takeUntil требуется излучение, поэтому вам нужно next() что-то (даже undefined). complete() является избыточным, учитывая, что никто не слушает это событие. - person Mark van Straten; 04.01.2021
comment
Означает ли это, что нам не нужны this.destroy $ .next () и this.destroy $ .complete () в ngOnDestroy () при использовании takeUntil? - person Fredrick; 05.01.2021

Лично я также выбираю вариант 1, который заключается в использовании takeUntil.

Однако важно, чтобы вы поместили оператор takeUntil() в последний оператор RxJS в последовательности каналов, как описано в здесь.

Например, если мы не поместим takeUntil() в качестве последнего оператора, абонент останется подписанным на следующий оператор switchMap():

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

Следовательно, правильный способ сделать это:

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

Брайан Лав написал действительно хорошую статью о различных способах (включая unsubscribe() и оператор takeUntil()) отказа от подписки на наблюдаемые объекты. Я бы порекомендовал вам тоже взглянуть на него.

person wentjun    schedule 17.10.2019
comment
Должны ли мы использовать this.destroy$.next() и this.destroy$.complete() в ngOnDestroy() при использовании takeUntil? - person Fredrick; 26.12.2020
comment
@ Фредрик, да, мы должны! Извините, мой ответ не отражает этого. - person wentjun; 01.01.2021
comment
Тогда я думаю, что лучше использовать unsubscribe вместо takeUntil. Потому что он показан как демонстрационная версия, но почти аналогичен тому, что мы также вызываем метод onDestroy. А это не? - person Fredrick; 01.01.2021
comment
@Fredrick: да, но если у вас есть несколько подписок в вашем компоненте, будет проще использовать подход takeUntil. Я считаю, что эта статья дает полное объяснение medium.com/@benlesh/rxjs-dont- отписаться-6753ed4fda87 - person wentjun; 01.01.2021
comment
Спасибо, но я также прочитал эту статью и вижу, что практически нет разницы между двумя подходами в отношении длин кода (один из них внутренний, другой - внешний блок). - person Fredrick; 01.01.2021
comment
takeUntil более гибкий. позиция takeUntil в операторах важна. но большую часть времени нам нужно, чтобы takeUntil был последним оператором и в это время равнялся отписке. - person Hamid Taebi; 20.07.2021

Для вашего примера я бы не выбрал ни один из 3 вариантов. Если вы хотите только присвоить значение, сохраните Observable и подпишитесь на него с помощью 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>
person MoxxiManagarm    schedule 17.10.2019
comment
Его примеры придуманы, чтобы предоставить контекст для вопроса, в то время как ваш ответ - это то, что нужно делать в реальной ситуации, но на самом деле он не помогает с конкретным вопросом. - person claudekennilol; 10.03.2021