Поиск по сайту:

Как использовать оператор takeUntil RxJS для декларативного управления подписками


Введение

Angular обрабатывает отписку от наблюдаемых подписок, таких как возвращаемые из службы HTTP или при использовании асинхронного канала. Однако в других ситуациях может быстро стать сложно управлять всеми подписками и гарантировать отмену подписки на те из них, которые имеют длительный срок действия. Политика отказа от большинства подписок также будет иметь свои проблемы.

В этой статье вам будет представлен пример приложения Angular, которое использует ручную подписку и отмену подписки. Затем вы сравните его с примером приложения Angular, в котором используется оператор takeUntil для декларативного управления подписками.

Предпосылки

Если вы хотите следовать этой статье, вам понадобятся:

  • Некоторое знакомство с библиотекой RxJS, в частности с Observable и Subscription, будет полезным.
  • Некоторое знакомство с Apollo и GraphQL будет полезно, но не обязательно.

Это руководство было проверено с помощью Node v15.3.0, npm v6.14.9, @angular/core v11.0.4, rxjs v6.6.3, apollo-angular v2.1.0, graph-tag v2.11.0. Эта статья была отредактирована, чтобы отразить изменения при переходе с более ранних версий @angular/core и rxjs.

Отмена подписки вручную

Давайте начнем с примера, где вы будете вручную отписываться от двух подписок.

В этом примере код подписывается на watchQuery Apollo для получения данных из конечной точки GraphQL.

Код также создает наблюдаемый интервал, на который вы подписываетесь при вызове метода onStartInterval.

import { Component, OnInit, OnDestroy } from '@angular/core';

import { Subscription, interval } from 'rxjs';

import { Apollo } from 'apollo-angular';
import gql from 'graphql-tag';

@Component({ ... })
export class AppComponent implements OnInit, OnDestroy {
  myQuerySubscription: Subscription;
  myIntervalSubscription: Subscription;

  constructor(private apollo: Apollo) {}

  ngOnInit() {
    this.myQuerySubscription = this.apollo.watchQuery<any>({
      query: gql`
        query getAllPosts {
          allPosts {
            title
            description
            publishedAt
          }
        }
      `
    })
    .valueChanges
    .subscribe(({data}) => {
      console.log(data);
    });
  }

  onStartInterval() {
    this.myIntervalSubscription = interval(250).subscribe(value => {
      console.log('Current value:', value);
    });
  }

  ngOnDestroy() {
    this.myQuerySubscription.unsubscribe();

    if (this.myIntervalSubscription) {
      this.myIntervalSubscription.unsubscribe();
    }
  }
}

Теперь представьте, что у вашего компонента есть много похожих подписок. Это может быстро превратиться в процесс, обеспечивающий отмену подписки при уничтожении компонента.

Декларативное отписывание с помощью takeUntil

Решение состоит в том, чтобы составлять подписки с помощью оператора takeUntil и использовать субъект, который выдает истинное значение в ловушке жизненного цикла ngOnDestroy.

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

import { Component, OnInit, OnDestroy } from '@angular/core';

import { Subject, interval } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { Apollo } from 'apollo-angular';
import gql from 'graphql-tag';

@Component({ ... })
export class AppComponent implements OnInit, OnDestroy {
  destroy$: Subject<boolean> = new Subject<boolean>();

  constructor(private apollo: Apollo) {}

  ngOnInit() {
    this.apollo.watchQuery<any>({
      query: gql`
        query getAllPosts {
          allPosts {
            title
            description
            publishedAt
          }
        }
      `
    })
    .valueChanges
    .pipe(takeUntil(this.destroy$))
    .subscribe(({data}) => {
      console.log(data);
    });
  }

  onStartInterval() {
    interval(250)
    .pipe(takeUntil(this.destroy$))
    .subscribe(value => {
      console.log('Current value:', value);
    });
  }

  ngOnDestroy() {
    this.destroy$.next(true);
    this.destroy$.unsubscribe();
  }
}

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

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

Заключение

В этой статье вы узнали об использовании takeUntil для декларативной отмены подписки. Отказ от ненужных подписок способствует предотвращению утечек памяти. Декларативное отписывание позволяет не требовать ссылок на подписки.

Существуют и другие подобные операторы RxJS, такие как take, takeWhile и first, которые завершат наблюдаемое.

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