Декларативное управление подписками в Angular

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.

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

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

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

Читайте также: Основы GraphQL: как работает ApolloBoost

Код также создает наблюдаемый объект, на который вы подписываетесь при вызове метода 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.

Читайте также: Что такое хуки жизненного цикла в Angular

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

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, которые завершают наблюдаемые объекты.

Tags: ,

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