[ПЕРЕВОД] Встречайте поведение поведения, воспроизводимые в RXJS

RxJS

Оригинальная ссылка:Understanding rxjs BehaviorSubject, ReplaySubject and AsyncSubject
Оригинальный автор:Luuk Gruijs; Опубликовано 4 мая 2018 г.
Переводчик:yk; Если вам нужно перепечатать, пожалуйста, укажитепроисхождение, Спасибо за ваше сотрудничество!

Роль Subject заключается в реализации многоадресной рассылки Observables. Поскольку его выполнение Observable совместно используется несколькими подписчиками, это гарантирует, что каждый подписчик получает абсолютно одинаковые данные. Мало того, что многоадресная рассылка может быть достигнута с помощью Subject, RxJS также предоставляет несколько вариантов Subject для работы с различными сценариями, а именно: BehaviorSubject, ReplaySubject и AsyncSubject.

Если вы не знаете, что такое Subject, я предлагаю вам сначала прочитать мою последнюю статью:Знакомство с темой в rxjs. Если вы считаете, что все в порядке, давайте продолжим!

фотография:Cory Schadt, отUnsplash

BehaviorSubject

BehaviorSubject — это один из вариантов Subject.Свойство BehaviorSubject заключается в том, что он хранит «текущее» значение. Это означает, что вы всегда можете напрямую получить последнее сгенерированное значение BehaviorSubject.

Есть два способа получить «текущее» значение BehaviorSubject: получить доступ к его.valueсвойства или подписаться напрямую. Если вы решите подписаться, то BehaviorSubject отправит текущее сохраненное значение непосредственно подписчику, независимо от того, насколько «старым» является значение. См. пример ниже:

import * as Rx from "rxjs";

const subject = new Rx.BehaviorSubject(Math.random());

// 订阅者 A
subject.subscribe((data) => {
  console.log('Subscriber A:', data);
});

subject.next(Math.random());

// 订阅者 B
subject.subscribe((data) => {
  console.log('Subscriber B:', data);
});

subject.next(Math.random());

console.log(subject.value)

// 输出
// Subscriber A: 0.24957144215097515
// Subscriber A: 0.8751123892486292
// Subscriber B: 0.8751123892486292
// Subscriber A: 0.1901322109907977
// Subscriber B: 0.1901322109907977
// 0.1901322109907977

Объясните подробно:

  1. Сначала мы создали тему и зарегистрировали в ней подписчика А. Поскольку subject является BehaviorSubject, абонент A немедленно получает начальное значение subject (случайное число) и одновременно печатает его.
  2. Затем субъект передает следующее значение. Абонент А снова печатает полученное значение.
  3. Во-вторых, подпишитесь на тему и назовите ее «Подписчик Б». Аналогично, подписчик B немедленно получит текущее сохраненное значение subject и распечатает его.
  4. subject снова передает следующее новое значение. В этот момент оба подписчика получат это значение и распечатают его.
  5. Наконец, мы просто получаем доступ.valueФорма свойства принимает текущее значение субъекта и печатает его. Это прекрасно работает в синхронных сценариях, потому что вам не нужно подписываться на Subject, чтобы получить его значение.

Кроме того, вы можете обнаружить, что BehaviorSubject необходимо установить с начальным значением при его создании. Этого очень сложно добиться в Observable, но в BehaviorSubject просто передайте значение.

Примечание переводчика: в текущей версии RxJSBehaviorSubject()Необходимо установить начальное значение, иначе это вызовет ошибку выполнения, а исходный текст этого не отражает. Поэтому я сделал много изменений в этом абзаце, чтобы не вводить читателя в заблуждение. смотрите подробностиBehaviorSubject.

ReplaySubject

В отличие от BehaviorSubject, ReplaySubject может отправлять «старые» данные новым подписчикам. Кроме того, у ReplaySubject есть дополнительная функция, заключающаяся в том, что он может записывать часть наблюдаемого выполнения, таким образом сохраняя некоторые старые данные для «воспроизведения» новым подписчикам.

При создании ReplaySubject вы можете указать объем данных для хранения и срок действия данных. То есть можно реализовать: «переигрывать» новым подписчикам последние пять транслируемых значений в течение секунды перед подпиской. Пример кода выглядит следующим образом:

import * as Rx from "rxjs";

const subject = new Rx.ReplaySubject(2);

// 订阅者 A
subject.subscribe((data) => {
  console.log('Subscriber A:', data);
});

subject.next(Math.random())
subject.next(Math.random())
subject.next(Math.random())

// 订阅者 B
subject.subscribe((data) => {
  console.log('Subscriber B:', data);
});

subject.next(Math.random());

// Subscriber A: 0.3541746356538569
// Subscriber A: 0.12137498878080955
// Subscriber A: 0.531935186034298
// Subscriber B: 0.12137498878080955
// Subscriber B: 0.531935186034298
// Subscriber A: 0.6664809293975393
// Subscriber B: 0.6664809293975393

Просто прочитайте код:

  1. Мы создаем ReplaySubject и указываем, что он хранит только значение двух последних трансляций;
  2. Подпишитесь на тему и назовите ее Подписчик А;
  3. субъект транслирует три раза подряд, и подписчик А также будет печатать его три раза подряд;
  4. На этом шаге ReplaySubject делает свое волшебство. Мы снова подписываемся на субъект и называем его Subscriber B, потому что мы указали субъекту для хранения двух последних широковещательных значений, поэтому субъект «повторит» последние два значения для Subscriber B. Мы видим, что подписчик B печатает эти два значения сразу;
  5. Последняя трансляция темы, оба подписчика получают значение и печатают его.

Ранее вы также можете установить время срока действия данных воспроизведения. Давайте посмотрим на этот пример:

import * as Rx from "rxjs";

const subject = new Rx.ReplaySubject(2, 100);

// 订阅者A
subject.subscribe((data) => {
  console.log('Subscriber A:', data);
});

setInterval(() => subject.next(Math.random()), 200);

// 订阅者B
setTimeout(() => {
  subject.subscribe((data) => {
    console.log('Subscriber B:', data);
  });
}, 1000)

// Subscriber A: 0.44524184251927656
// Subscriber A: 0.5802631630066313
// Subscriber A: 0.9792165506699135
// Subscriber A: 0.3239616040117268
// Subscriber A: 0.6845077617520203
// Subscriber B: 0.6845077617520203
// Subscriber A: 0.41269171141525707
// Subscriber B: 0.41269171141525707
// Subscriber A: 0.8211466186035139
// Subscriber B: 0.8211466186035139

Также читайте код:

  1. Мы создали ReplaysUbject и уточняли его для хранения значения последних двух трансляций, но только для 100 мс;
  2. Подпишитесь на тему и назовите ее Подписчик А;
  3. Мы делаем эту тему транслируемой каждые 200 мс. Подписчик A будет получать значение и каждый раз печатать его;
  4. Мы настраиваем подписку субъекта снова после одной секунды выполнения программы и называем его Subscriber B. Это означает, что субъект провел трансляцию пять раз, прежде чем начал подписываться. Поскольку мы установили время истечения данных на 100 мс и интервал широковещательной рассылки на 200 мс при создании субъекта, подписчик B получит только последнее значение из первых пяти широковещательных сообщений после начала подписки.

AsyncSubject

И BehaviorSubject, и ReplaySubject могут использоваться для хранения некоторых данных, но AsyncSubject отличается. AsyncSubject отправит свое окончательное значение подписчикам только после завершения выполнения Observable. Пожалуйста, смотрите код:

import * as Rx from "rxjs";

const subject = new Rx.AsyncSubject();

// 订阅者A
subject.subscribe((data) => {
  console.log('Subscriber A:', data);
});

subject.next(Math.random())
subject.next(Math.random())
subject.next(Math.random())

// 订阅者B
subject.subscribe((data) => {
  console.log('Subscriber B:', data);
});

subject.next(Math.random());
subject.complete();

// Subscriber A: 0.4447275989704571
// Subscriber B: 0.4447275989704571

Хотя вывод этого кода невелик, давайте интерпретируем его как обычно:

  1. Создать асинхронный субъект;
  2. Подпишитесь на тему и назовите ее Подписчик А;
  3. тема вещает три раза подряд, но ничего не происходит;
  4. снова подпишитесь на тему и назовите ее подписчиком Б;
  5. тема снова вещает, но по-прежнему ничего не происходит;
  6. тема завершена. Затем оба подписчика получают входящее значение и выводят его на терминал.

В заключение

BehaviorSubject, ReplaySubject и AsyncSubject могут использоваться для многоадресной рассылки точно так же, как и Subject. Каждый из них имеет некоторые дополнительные функции для различных сценариев.

译者注:这个结论好敷衍。 . .