Субъекты поведения и воспроизведения RxJS
Development, Java | Комментировать запись
Субъект в 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: RxJS