Какой подход лучше для уничтожения наблюдаемого, предусмотренного для оператора takeUntil, и почему? - PullRequest
5 голосов
/ 05 июля 2019

У меня есть вопрос об одном из общих шаблонов отказа от подписки с оператором takeUntil для Angular и RxJ.В этой статье она находится на третьей позиции.Например, у нас есть такой код в классе компонентов:

  private destroy$: Subject<boolean> = new Subject();

  ngOnInit() {
     this.control.
     .pipe(takeUntil(this.destroy$)
     .subscribe(doSmthngFunc); 
  }

  ngOnDestroy() {
    this.destroy$.next(true);
    // Which next line of code is correct?
    // this.destroy$.complete()     // this one?
    // this.destroy$.unsubscribe()  // or this one?
  }

Первая строка this.destroy $ .next (true) полностью ясна.Но второй нет.Если мы посмотрим на реализацию этих методов, мы обнаружим, что они имеют несколько похожее поведение. complete (): unsubscribe ():

Как я понимаю, предпочтительным является семантическое завершение (), поскольку мы вызываем next () в первый и последний раз во времяжизнь компонента и затем мы закончили с этим Предметом, обработанным как Наблюдаемый и можем вызвать complete ().Эти методы принадлежат наблюдателю, а отписки принадлежат наблюдаемому, и у нас нет подписок, от которых можно отказаться.Но под капотом эти методы имеют похожий код:

    this.isStopped = true; // both

    this.observers.length = 0; // complete
    this.observers = null;     // unsubscribe

    this.closed = true;        // only unsubscribe

Теоретически complete () имеет задержанный эффект, так как может вызывать complete () для каждого подписанного наблюдателя, но у нас нет наблюдателей на destroy $.Так что вопрос - какой путь более предпочтителен, менее подвержен ошибкам и почему?

Ответы [ 3 ]

0 голосов
/ 06 июля 2019

Оба утверждения будут иметь желаемый эффект.Афаик, нет технической причины не использовать unsubscribe.Семантически - как вы упоминали - имеет смысл использовать complete.

Субъект является одновременно наблюдаемым (посылающим) и наблюдателем (слушающим), и эта двойственность отражается здесь.next и complete оба относятся к «отправляющей» стороне вещей.complete сигнализирует «этот поток не будет отправлять дальнейшие значения».unsubscribe является частью интерфейса «прослушивания» и имеет соответственно другое значение: «не уведомлять меня о каких-либо дальнейших выбросах».

Редактировать: при повторном чтении, я вижуВы уже включили это различие в вопрос, так что мой ответ, вероятно, мало что для вас добавляет :(. Я действительно думаю, что семантика сама по себе достаточно важна для использования здесь полной надписки и не вижу фактического рискаиспользовать один поверх другого в этом паттерне. Надеюсь, что это все еще несколько полезно!

0 голосов
/ 06 июля 2019

Уничтожение компонента является единственным событием.

   this.destroy$.next();
   this.destroy$.complete();

Гарантирует, что субъект излучает только один раз и завершает .

Например;

    const destroy$ = new Subject();

    destroy$.subscribe(v => console.log("destroyed"));

    destroy$.next();
    destroy$.complete();
    destroy$.next();

    // the above prints "destroyed" only once.

Это не техническое требование для завершения, но если вы этого не сделаете, то бизнес-логика, которая зависит от завершения вместо передачи, не всегда будет работать и может привести к утечке памяти.

Например, следующее будет утечка памяти в RxJs.

   destroyed$.subscribe(() => {
       console.log('This might leak memory');
   });

Вышеуказанное может привести к утечке памяти, поскольку подписка никогда не заканчивается и наблюдаемое никогда не завершается. Вы можете устранить утечку, добавив оператор first() или убедившись, что тема завершена. RxJS не знает, что субъект будет излучать только одно значение, и поэтому вы должны сказать это. В противном случае подписчики остаются привязанными к стековому фрейму и не собираются мусором. Таким образом, хотя сборщик мусора может собирать компонент после его использования, если что-либо ссылается на фрейм стека подписчика, эта подписка действует.

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

this.destroy $ .unsubscribe ()

Вызов отмена подписки по теме может не повлиять на последующих операторов, которые создают внутренние подписки. Например, switchMap() и mergeMap() создают внутренние подписки.

Таким образом, вы не можете эффективно управлять подписками выше. Отказаться от подписки, созданной при вызове метода subscribe(), лучше, потому что он последний в цепочке операторов.

0 голосов
/ 06 июля 2019

Я не думаю, что вам нужно делать намного больше, чем destroy$.next();. destroy$ - это Subject, который вы создали в своем классе, и единственное, за что он отвечает, - это прерывание вашей подписки. Никто, но вашему классу разрешено прикасаться к нему (так как он приватный)

Согласно статье, лучше сделать destroy$.complete(), чтобы избежать утечек памяти. Я не думаю, что использование unsubscribe() имеет смысл. Если кто-то подписался на destroy$, вы должны сохранить это в подписке и отказаться от подписки в методе ngOnDestroy() вызывающих абонентов. Тем не менее, поскольку вы используете takeUntil, отпадает необходимость в отписке.

...