Субъекты поведения и воспроизведения RxJS

Субъект в RxJS — это особый гибрид, который может выступать и в роли наблюдаемого объекта, и в роли наблюдателя одновременно. Таким образом, данные могут быть отправлены в субъект, а подписчики субъекта, в свою очередь, получат эти отправленные данные.

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

В этом руководстве вы узнаете, что такое субъект, субъект поведения и субъект воспроизведения.

Требования

Для дальнейшей работы вам понадобится базовое знакомство с наблюдаемыми объектами и наблюдателями RxJS.

Это руководство было протестировано с помощью rxjs v7.3.0.

Использование субъектов

Создание субъекта начинается с нового экземпляра Subject:

const mySubject = new Rx.Subject();

Вы можете создать несколько подписок, и внутри субъекта будет храниться их список:

const subscription1 = mySubject.subscribe(x => console.log(`${x} ${x}`));

const subscription2 = mySubject.subscribe(x => console.log(x.toUpperCase()));

Поместить данные в субъект можно с помощью метода next:

mySubject.next('Hello!');

Запуск этого скрипта приведет к следующему выводу:

Hello! Hello!
HELLO!

Для subscription1 этот код примет ввод и отобразит его дважды. Для subscription2 он примет ввод и применит toUpperCase().

Когда данные вводятся в субъект, он просматривает свой внутренний список подписок и затем помещает данные в каждую из них через next.

Отправка данных в подписки

Следующий пример демонстрирует, как данные передаются в подписки:

const mySubject = new Rx.Subject();

mySubject.next(1);

const subscription1 = mySubject.subscribe(x => {
  console.log('From subscription 1:', x);
});

mySubject.next(2);

const subscription2 = mySubject.subscribe(x => {
  console.log('From subscription 2:', x);
});

mySubject.next(3);

subscription1.unsubscribe();

mySubject.next(4);

В этом примере мы получим в консоли такой результат:

From subscription 1: 2
From subscription 1: 3
From subscription 2: 3
From subscription 2: 4

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

Многоадресная рассылка данных

По-настоящему силу субъектов можно оценить при многоадресной рассылке, когда субъект передается наблюдаемому объекту как наблюдатель – то есть когда наблюдаемый объект передает данные, эти данные поступают многоадресно всем подпискам субъекта.

В данном примере наблюдаемая функция trickleWords выдает слово каждые 750 мс.

const mySubject = new Rx.Subject();

const words = ['Hot Dog', 'Pizza', 'Hamburger'];

const trickleWords = Rx.Observable.zip(
  Rx.Observable.from(words),
  Rx.Observable.interval(750),
  word => word
);

const subscription1 = mySubject.subscribe(x => {
  console.log(x.toUpperCase());
});

const subscription2 = mySubject.subscribe(x => {
  console.log(
    x
      .toLowerCase()
      .split('')
      .reverse()
      .join('')
  );
});

trickleWords.subscribe(mySubject);

Мы получим следующий вывод после того, как все значения будут переданы:

HOT DOG
god toh
PIZZA
azzip
HAMBURGER
regrubmah

Для subscription1 массив words был изменен с помощью toUpperCase(). Для subscription2 этот массив был изменен с помощью методов toLowerCase() и “reverse()`.

Использование asObservable

Оператор asObservable можно использовать для преобразования субъекта в наблюдаемый объект. Это может быть полезно, если вы хотите предоставить данные из субъекта, но в то же время предотвратить непреднамеренное попадание данных в субъект:

const mySubject = new Rx.Subject();
const myObservable = mySubject.asObservable();

mySubject.next('Hello');
myObservable.next('World!');

В результате получится:

TypeError: myObservable.next is not a function

myObservable не поддерживает next, error и complete.

Обработка ошибок

Когда субъект завершает работу или выдает ошибку, все внутренние подписки делают то же самое:

const mySubject = new Rx.Subject();

const subscription1 = mySubject.subscribe(null, error =>
  console.log('From subscription 1:', error.message)
);

const subscription2 = mySubject.subscribe(null, error =>
  console.log('From subscription 2:', error.message)
);

mySubject.error(new Error('Error!'));

Вы получите следующий результат:

From subscription 1: Error!
From subscription 2: Error!

Как видите, в результате были сгенерированы сообщения об ошибках.

Использование субъектов воспроизведения

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

Вот пример использования субъектов воспроизведения, где буфер из 2 предыдущих значений сохраняется и выдается при появлении новых подписок:

const mySubject = new Rx.ReplaySubject(2);

mySubject.next(1);
mySubject.next(2);
mySubject.next(3);
mySubject.next(4);

mySubject.subscribe(x => {
  console.log('From subscription 1:', x);
});

mySubject.next(5);

mySubject.subscribe(x => {
  console.log('From subscription 2:', x);
});

Мы получим следующий результат:

From subscription 1: 3
From subscription 1: 4
From subscription 1: 5
From subscription 2: 4
From subscription 2: 5

Как видите, был сохранен буфер из 2 значений.

Использование субъектов поведения

Субъекты поведения аналогичны субъектам воспроизведения, но они повторно выдают только последнее сгенерированное значение или значение по умолчанию, если ранее не было сгенерировано никакого значения:

const mySubject = new Rx.BehaviorSubject('Hello!');

mySubject.subscribe(x => {
  console.log('From subscription 1:', x);
});

mySubject.next(5);

mySubject.subscribe(x => {
  console.log('From subscription 2:', x);
});

В результате получаем:

From subscription 1: Hello!
From subscription 1: 5
From subscription 2: 5

Заключение

В этом мануале мы обсудили субъекты и их виды: субъекты поведения и воспроизведения.

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

Tags:

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