В чем разница между 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 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарівДякую, друже! Твоя стаття допомогла пройти собес в круту фірму.
Спасибо за статью, лучшее обьяснениние что находил.
тащемто 4, про самый обычный Subject стоило упоминуть. И про разницу между ним и Бихейвор. Они самые часто используемные, потому это важно.
о них и так понятно
Спасибо большое! Все очень понятно изложено и примеры классные!!!
Дякую, не погана стаття!
Було би ще цікаво почитати про оператори :)