Как автоматически отписаться от RxJs Observables?

Apr 21, 2019

Всегда ли отписываться и почему необходимо отписываться от HttpClient?

Потому что ваш компонент может быть удален из DOM до завершения подписки. Например, вы делаете http-запрос, которые идет некоторое время и пользователь делает клик с целью покинуть компонент, хотя запрос по-прежнему работает. Поздравляю, у вас появилась подписка-призрак, которая может привести к утечки памяти, так как данный observable никогда не будет отменен.

Итак, вы всегда должны отписываться в ngOnDestroy.

Как отписываться?

Первое, чтобы я порекомендовал, это сохранить все подписке в массиве, чтобы было проще их перебрать и отписаться.

Я использовал реализацию от Ward Bell’s SubSink. Это простой класс, который хранит ваши подписки и предоставляет метод для отписки.

export class SomeComponent implements OnDestroy {
  private subs = new SubSink();

  ...
  // easy syntax
  this.subs.sink = observable$.subscribe(...);

  // Same job done with add()
  this.subs.add(observable$.subscribe(...));

  // Can add multiple subscriptions too
  this.subs.add(
    observable$.subscribe(...),
    anotherObservable$.subscribe(...)
  );

  ...

  // Unsubscribe when the component dies
  ngOnDestroy() {
    this.subs.unsubscribe();
  }
}

Можем ли мы улучшить это?

Конечно. Первый вариант, который бы я порекомендовал, если вы не хотите отписываться - не подписываться. Звучит глупо, но используя асинхронный pipe вы автоматизируете процесс подписки / получения данных / отписки практически без кода:

    <table mat-table [dataSource]="filteredEntries$ | async">

Но async pipe не работает, если я хочу что-то сделать с данными в моем TS коде

Это неверное оправдание, чтобы не использовать async pipe, потому что вы можете зарегестрировать некоторый побочный эффект, используя оператор tap:

filteredEntries$: Observable<any> = someObservable$.pipe(
   tap(data => // Do something with your data here)
);

Что если я и вправду не могу использовать async pipe, но по-прежнему хочу писать меньше кода?

Чтобы не писать ngOnDestroy каждый раз по новой я написал класс-адаптер, в котором все это реализовано:

import { OnDestroy } from '@angular/core';
import { SubSink } from './sub-sink';

/**
* A class that automatically unsubscribes all observables when
* the object gets destroyed
*/
export class UnsubscribeOnDestroyAdapter implements OnDestroy {

/**The subscription sink object that stores all subscriptions */
subs = new SubSink();

/**
* The lifecycle hook that unsubscribes all subscriptions
* when the component / object gets destroyed
*/
ngOnDestroy(): void {
   this.subs.unsubscribe();
}

Теперь все компоненты / сервисы / директивы / фильтры, которые имеют подписку должны унаследовать UnsubscribeOnDestroyAdapter. Таким образом отпадает необходимость в реализации везде ngOnDestroy, это сделано в родительском классе.

Я также использую SubSink, который обявлен один раз. Далее все что нужно сделать в каком-либо компоненте:

import { Component, OnInit } from '@angular/core';
import { UnsubscribeOnDestroyAdapter} from '../unsubscribe-on-destroy-adapter';

@Component({...})
export class MyComponent extends UnsubscribeOnDestroyAdapter {

   constructor() {
     super();
     this.subs.sink = someObservable$.subscribe(...);
   }
}

источник

Добавить комментарий