Управление подпиской RxJS с помощью takeUntil

Angular обрабатывает отмену прежних подписок на observable (это может быть, к примеру, подписка на события сервиса 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.

Читайте также: Введение в GraphQL: преимущества и недостатки

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

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

В данном случае код подписывается на 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 и использовать Subject, который выдает truthy значение в хуке жизненного цикла 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: ,

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