RxJS: разбор Subject`ов - Блог ITVDN
ITVDN: курсы программирования
Видеокурсы по
программированию

Доступ более чем к 7700 видеоурокам от $19.99

Подписка
ITVDN logo
Видеокурсы по
программированию

Доступ более чем к 7700 видеоурокам от $19.99

Подписка

Я был свидетелем многих вопросов, связанных с Subject`ами на Stack Overflow. Недавно я увидел одного разработчика, который интересовался, как, собственно говоря, работает AsyncSubject. Вопрос заставил меня написать эту статью, чтобы показать, почему необходимо использовать различные типы Subject`ов и как их использовать.

 

Когда мы используем Subject`ы?

В своей статье Бен Леш утверждал, что:

… [мультикастинг] является основной причиной использования Subject`ов в RxJS.

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

Подобная связь «наблюдаемости» с «наблюдаемыми» и есть сутью Subject`ов. Причина этого заключается в том, что де-факто Subject`ы являются одновременно «наблюдаемостью» и «наблюдателями».

 

Как они могут быть использованы?

В качестве примера давайте рассмотрим компонент Angular awesome-component. Наш компонент выполняет определенную работу и содержит в себе внутреннюю «наблюдаемость», что производит определенное значение при работе с ней пользователя.

Чтобы позволить родительским компонентам получить доступ к «наблюдаемости», awesome-component принимает «наблюдателя», что вводит свойство и что подписывается, в свою очередь, на «наблюдаемость». Это значит, что отныне родитель может соединиться с «наблюдаемостью» при помощи спецификации «наблюдателя» - что-то наподобие этого:

Так как теперь «наблюдатель» «обвязан», родитель соединен и получает значения от awesome-component. Впрочем по сути это то же самое, как  если бы awesome-component производил значения через подписанные события. Так почему же мы здесь не используем события?

Дело в том, что «наблюдаемостями» проще управлять. К примеру, чтобы добавить фильтры, нам необходимо использовать лишь несколько операторов. Но немаловажный нюанс: родительский компонент имеет «наблюдателя» – не «наблюдаемость», так как в таком случае мы можем применять операторы?

Subject`ы - это одновременно и «наблюдаемости», и «наблюдатели», поэтому, когда мы создаем Subject, он может быть использован по отношению к awesome-component в качестве «наблюдателя» или работать с компонентом как с «наблюдаемостью». Что-то наподобие этого:

Subject соединяет «наблюдателя» по принципу «делай-все-что-хочешь-со-значением» с «наблюдаемостью» в виде awesome-component. Но здесь применяется набор операторов родителей компонента.

 

Композиция различных «наблюдаемостей»

При помощи Subject`а для композиции «наблюдаемости» awesome-component может быть использован в разных целях разными компонентами. К примеру, другой компонент может быть заинтересован только в последнем сгенерированном значении. Для этого нужно использовать last-оператор:

Что интересно, это не единственный способ получения последнего значения: мы просто можем использовать другой Subject. К примеру, при помощи AsyncSubject код будет выглядеть следующим образом, так как он производит только последнее полученное значение:

Но, если использование AsyncSubject равнозначно композиции «наблюдаемости» при помощи использования Subject и last-оператора, зачем усложнять RxJS лишним классом?

Ну, в основном потому что Subject`ты предназначены для мультикастинга.

В данном случае два способа эквивалентны, потому что здесь есть только один подписчик. В ситуации с применением мультикастинга здесь было бы несколько подписчиков и применять оператор last здесь было бы нецелесообразно.

Теперь же давайте рассмотрим мультикастинг более детально.

 

Как Subject`ы используются непосредственно в RxJS?

Ядро инфраструктуры мультикастинга RxJS исполняется при помощи оператора multicast. Multicast вообще применяется к ключевым «наблюдаемостям», принимает Subject и возвращает полученную из Subject`а «наблюдаемость».

Оператор multicast чем-то похож на awesome-component. Мы можем принимать «наблюдаемость», чье поведение зависит от принимаемого Subject`а.

Ситуации, когда базовый Subject передается multicast:

  • Подписчики мультикаст-«наблюдаемости» принимают оповещения типа next, error, complete.
  • «Поздние» подписчики , другими словами, те, которые подписались после оповещений error, complete, – так же в свою очередь принимают эти оповещения.

Важно отметить, что пока мультикастинг не передал factory, «поздние» подписчики не работают с другими подписками на источник.

Чтоы произвести композицию по отношению к мультикаст-«наблюдаемости», что передает последнее оповещение next ко всем подписчикам, недостаточно просто применить last-оператор к «наблюдаемости», созданной при помощи Subject. «Поздние» подписчики подобной «наблюдаемости» не получат последнее next-оповещение. Они получат только complete.

Специально для этого оповещения должны храниться в состоянии Subject`а. Именно это делает класс AsyncSubject и именно для этого мы используем AsyncSubject в подобной ситуации.

 

Что касательно других классов Subject`ов?

Существует двое других вариантов Subject`ов: BehaviorSubject и ReplaySubject.

Чтобы понимать BehaviorSubject лучше, давайте рассмотрим пример:

Здесь родительский компонент соединяется с awesome-component при помощи Subject и применяет оператор startWith. Этот оператор обеспечивает надежный прием значения “awesome” вместе со значениями, сгенерированными awesome-component – в случае, конечно, если они таки были сгенерированы.

Подобно тому, как AsyncSubject используется вместо обычного Subject`а и оператора last, BehaviorSubject может заменить собой оператора startWith и Subject`а – так как его конструктор принимает значение, которое было бы в противном случае направлено к startWith.

В случае с использованием BehaviorSubject все подписчики получат начальное значение. Это возможно потому, что BehaviorSubject хранит значение переменной в своем состоянии.

По той причине, что концепция «переигрывания» уже полученных оповещений внедрена в мульти-подписку, аналогии с единым подписчиком для ReplaySubject просто не существует. Так же, как и BehaviorSubject, переменные хранятся в состоянии Subject`а.

 

Итак, как мы используем эти Subject`ы?

Мы увидели, какие бывают Subject`ы и для чего они используются. Но как они должны быть использованы? Что ж, как бы это ни было парадоксально, но класс Subject – это тот класс, который вам, вероятно, никогда не придётся использовать.

Subject работает прекрасно при связывании «наблюдателя» с «наблюдаемостью». Но для ситуаций с мультикастингом существуют альтернативы.

RxJS содержит операторы мультикастинга, которые используют различные Subject – классы, причем точно так же, как я могу использовать генераторы «наблюдаемостей» RxJS (fromEvent) над вызовами Observable.create. Но для ситуаций с мультикастингом я все же предпочитаю использовать следующие операторы:

  • Publish или share могут быть использованы вместо Subject;
  • publishBehavior может быть использован вместо BehaviorSubject;
  • publishLast может быть использован вместо AsyncSubject;
  • publicReplay или shareReplay могут быть использованы вместо ReplaySubject.

 

Автор перевода: Евгений Лукашук

Источник

СТАТЬИ ПО СХОЖЕЙ ТЕМАТИКЕ
ВИДЕО КУРСЫ ПО СХОЖЕЙ ТЕМАТИКЕ
КОММЕНТАРИИ И ОБСУЖДЕНИЯ

ПОДПИСКА НА ITVDN ВЫГОДА ДО 29.95$ НА ОБУЧЕНИЕ ПРЕСТИЖНЫМ ПРОФЕССИЯМ!

1 месяц19.99$
подписка

легкий старт в обучении

3 месяц49.99$
подписка

выгода от подписки до9.98$

6 месяц89.99$
подписка

выгода от подписки до29.95$