В чем разница между Subject, BehaviorSubject, ReplaySubject и AsyncSubject
Привет! Меня зовут Владислав Василенко, я сотрудничаю с компанией Dev.Pro в роли Software Engineer. В своей профессиональной деятельности я часто использую RxJS
, который сложно представить без Observers
и Observables
. Обычно с ними не возникает каких-то сложностей или вопросов. Говоря же о Subjects
, часто, особенно начинающие разработчики, не до конца понимают разницу между их видами. Конечно, на это есть ряд причин, начиная с того, что каждый имеет свои особенности и случаи применения, и заканчивая тем, что с Subjects
мы сталкиваемся реже, а какие-то его виды, возможно, вообще не используем так часто, как хотелось бы.
В этой статье я не только дам определения всем этим понятиям, но и постараюсь привести примеры кода и аналогии из жизни, которые должны максимально упростить и облегчить эту тему для понимания.
Если вы начинающий разработчик, хотите просто освежить в памяти эти понятия или же вас заинтересовало название статьи, то вы оказались в правильном месте.
Прежде чем приступить к основному материалу, предлагаю сперва рассмотреть следующие пункты, чтобы избежать возможных вопросов и последующих недопониманий:
- Реактивное программирование.
Observers
иObservables
.Observables
илиPromises
.
Что такое реактивное программирование
Сегодня на каждом шагу можно услышать или прочитать что-то про реактивность в программировании. Кто-то с легкостью справляется с этой темой, а кто-то застревает уже на определении.
Согласно Википедии — это парадигма программирования, ориентированная на потоки данных и распространение изменений. Если упростить, то это любое приложение, управляемое потоком событий (клик мышки), за которым наблюдают подписчики и реагируют на то или иное событие.
Пример из жизни. Представьте магазин с 2 кассами, где на одной скопилась большая очередь, а вторая временно закрыта. Каждый новый человек становится в конец очереди и произносит какую-то фразу. Оператор на второй кассе внимательно слушает (подписывается) и, если кто-то произносит специальную фразу (событие), то такого посетителя приглашают оплатить покупки на свободную (вторую) кассу.
Кассир — это наблюдатель или Observer
, а фразы людей из очереди — это поток событий или Observable
.
Что такое Observers
и Observables
Observer
(наблюдатель) — это объект-обработчик потока данных, который ему передает Observable
.
Observable
(наблюдаемый) — это объект-передатчик потока данных, их существует 2 типа:
Cold
— начинает потоковую передачу данных после вызоваsubscribe()
.Hot
— передается сразу после его создания, даже если ни один подписчик не заинтересован в данных.
Observables
выполняют 3 основных действия:
- передача следующего элемента;
- сообщение об ошибке;
- уведомление о завершении потоковой передачи.
Observer
же, в свою очередь, для обработки вышеуказанных действий использует 3 функции:
next()
— обработка следующего элемента;error()
— обработка ошибок;complete()
— вызов после завершения потока данных.
Observables или Promises
Возможно, у кого-то возникнет вопрос: «А зачем нужны Observables
, если есть Promises
?»
С одной стороны, вопрос действительно логичный, ведь Promises
также помогают работать с асинхронными запросами.
С другой стороны, немного разобравшись в различиях между этими двумя понятиями, ответ становится более менее очевидным:
Observables
могут определять аспекты асинхронного поведения как при инициализации, так и при своем разрушении.Observables
можно отменить.Observables
можно повторить (retry
иretryWhen
) и сделать это легко, в отличие отPromises
.
Теперь, когда базовая теория рассмотрена, можно уже перейти к основной теме статьи, а начнем мы опять же с определения.
Что такое Subject
Subject
— это особый объект из RxJS, ведь он является Observable
(может отправлять данные) и Observer
(может подписываться на поток данных) одновременно. Выделяют 3 типа Subjects
, о каждом из которых мы сейчас поговорим:
1. BehaviorSubject
— вариант Subject
, который требует начального значения и имеет свойство хранить текущее значение.
import { BehaviorSubject } from 'rxjs'; const subject = new BehaviorSubject(0); subject.subscribe(value => console.log(`Current value is ${value}`)); // Current value is 0 subject.next(1) // Current value is 0 // Current value is 1 subject.next(2) // Current value is 0 // Current value is 1 // Current value is 2 setTimeout(()=> { subject.subscribe(value => console.log(`New current value is ${value}`)); // Current value is 0 // Current value is 1 // Current value is 2 // New current value is 2 subject.next(3) }, 1000); // Current value is 0 // Current value is 1 // Current value is 2 // New current value is 2 // Current value is 3 // New current value is 3
Когда использовать? В случаях, если нам важно иметь начальное значение у подписок, то BehaviorSubject
просто незаменим. Таким образом, подписка всегда будет иметь последнее (текущее) значение, с которым можно будет работать.
Пример из жизни. Представьте, что вы попали на середину сеанса в кинотеатре. Без вступления или предисловия вам будет тяжело разобраться в контексте фильма, поэтому перед сеансом вы решили посмотреть трейлер. Этот трейлер или начальное значение очень похож на BehaviorSubject
.
2. ReplaySubject
— еще один вид Subject, который также может отправлять старые значения новым подписчикам, как и BehaviorSubject
. Кроме этого, у него есть одна особенность — он может запоминать часть старых значений и отправлять их новым подписчикам. Создавая ReplaySubject
, можно указать количество таковых значений.
import { ReplaySubject } from 'rxjs'; const subject = new ReplaySubject(2); subject.subscribe(value => console.log(`Current value is ${value}`)); subject.next(1) // Current value is 1 subject.next(2) // Current value is 1 // Current value is 2 subject.next(3) // Current value is 1 // Current value is 2 // Current value is 3 subject.next(4) // Current value is 1 // Current value is 2 // Current value is 3 // Current value is 4 subject.subscribe(value => console.log(`New Current value is ${value}`)); // Current value is 1 // Current value is 2 // Current value is 3 // Current value is 4 // New current value is 3 // New current value is 4 subject.next(5) // Current value is 1 // Current value is 2 // Current value is 3 // Current value is 4 // New current value is 3 // New current value is 4 // Current value is 5 // New current value is 5
Когда использовать? Представим, что мы хотим сохранить последние 10 значений, которые были получены за последнюю минуту перед новой подпиской. Например, новый пользователь добавился в чат, и мы хотим показать ему последнии сообщения. ReplaySubject
позволяет реализовать это самым простым способом.
Пример из жизни. Представьте, что вы опоздали на какую-то очень важную лекцию, где необходимо было присутствовать с самого начала. Диктофон, на который ваш друг записывал эту лекцию с самого начала или с того момента, как вы попросили его, это и есть ReplaySubject
.
3. AsyncSubject
— разновидность Subject
, чья особенность заключается в том, что он передает только последнее значение всем своим подписчикам и только после завершения выполнения Observable
.
import { AsyncSubject } from 'rxjs'; const subject = new AsyncSubject(); subject.subscribe(value => console.log(`Current value is ${value}`)); subject.next(1) subject.next(2) subject.next(3) subject.next(4) subject.subscribe(value => console.log(`New Current value is ${value}`)); subject.next(5) subject.complete(); // Current value is 5 // New current value is 5
Когда использовать? Пожалуй, самым подходящим вариантом использования AsyncSubject
будет обработка HTTP-запроса, содержащего только один результат, ведь данный вид Subjects
:
- выдаст только одно значение;
- завершится, поэтому не нужно будет отписываться;
- другие
Observers
смогут подписаться даже после завершения запроса.
Пример из жизни. Представьте, что вы пропустили какое-то важное пятичасовое обсуждение. Вы не хотите и от вас не требуется знать содержание всего обсуждения, но необходимо знать финальное решение, от которого зависят ваши дальнейшие задачи. Финальное решение или итог всего обсуждения и есть AsyncSubject
.
Выводы
Поздравляю тех, кто дошел до этого момента, ведь теперь различные виды Subjects
должны быть куда понятнее для вас. Для закрепления информации, давайте кратко повторим основные различия:
BehaviorSubject
— используем тогда, когда нам важно иметь начальное значение для подписки.ReplaySubject
— используем тогда, когда нам нужно запомнить какое-то количество значений из потока данных и передать в новую подписку.AsyncSubject
— используем тогда, когда нам нужно только последнее значение, при этом все остальные нам не важны.
Также советую ознакомиться с официальной документацией, там есть как краткое объяснение, так и примеры использования:
На этом статья подходит к своему логическому завершению, а если у вас остались какие-то вопросы, комментарии или предложение, то с радостью готов обсудить!
6 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів