В чем разница между Subject, BehaviorSubject, ReplaySubject и AsyncSubject

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Привет! Меня зовут Владислав Василенко, я сотрудничаю с компанией Dev.Pro в роли Software Engineer. В своей профессиональной деятельности я часто использую RxJS, который сложно представить без Observers и Observables. Обычно с ними не возникает каких-то сложностей или вопросов. Говоря же о Subjects, часто, особенно начинающие разработчики, не до конца понимают разницу между их видами. Конечно, на это есть ряд причин, начиная с того, что каждый имеет свои особенности и случаи применения, и заканчивая тем, что с Subjects мы сталкиваемся реже, а какие-то его виды, возможно, вообще не используем так часто, как хотелось бы.

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

Если вы начинающий разработчик, хотите просто освежить в памяти эти понятия или же вас заинтересовало название статьи, то вы оказались в правильном месте.

Прежде чем приступить к основному материалу, предлагаю сперва рассмотреть следующие пункты, чтобы избежать возможных вопросов и последующих недопониманий:

  • Реактивное программирование.
  • Observers и Observables.
  • Observables или Promises.

Что такое реактивное программирование

Сегодня на каждом шагу можно услышать или прочитать что-то про реактивность в программировании. Кто-то с легкостью справляется с этой темой, а кто-то застревает уже на определении.

Согласно Википедии — это парадигма программирования, ориентированная на потоки данных и распространение изменений. Если упростить, то это любое приложение, управляемое потоком событий (клик мышки), за которым наблюдают подписчики и реагируют на то или иное событие.

Пример из жизни. Представьте магазин с 2 кассами, где на одной скопилась большая очередь, а вторая временно закрыта. Каждый новый человек становится в конец очереди и произносит какую-то фразу. Оператор на второй кассе внимательно слушает (подписывается) и, если кто-то произносит специальную фразу (событие), то такого посетителя приглашают оплатить покупки на свободную (вторую) кассу.

Кассир — это наблюдатель или Observer, а фразы людей из очереди — это поток событий или Observable.

Что такое Observers и Observables

Observer (наблюдатель) — это объект-обработчик потока данных, который ему передает Observable.

Observable (наблюдаемый) — это объект-передатчик потока данных, их существует 2 типа:

  1. Cold — начинает потоковую передачу данных после вызова subscribe().
  2. Hot — передается сразу после его создания, даже если ни один подписчик не заинтересован в данных.

Observables выполняют 3 основных действия:

  • передача следующего элемента;
  • сообщение об ошибке;
  • уведомление о завершении потоковой передачи.

Observer же, в свою очередь, для обработки вышеуказанных действий использует 3 функции:

  1. next() — обработка следующего элемента;
  2. error() — обработка ошибок;
  3. 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 должны быть куда понятнее для вас. Для закрепления информации, давайте кратко повторим основные различия:

  1. BehaviorSubject — используем тогда, когда нам важно иметь начальное значение для подписки.
  2. ReplaySubject — используем тогда, когда нам нужно запомнить какое-то количество значений из потока данных и передать в новую подписку.
  3. AsyncSubject — используем тогда, когда нам нужно только последнее значение, при этом все остальные нам не важны.

Также советую ознакомиться с официальной документацией, там есть как краткое объяснение, так и примеры использования:

На этом статья подходит к своему логическому завершению, а если у вас остались какие-то вопросы, комментарии или предложение, то с радостью готов обсудить!

👍НравитсяПонравилось14
В избранноеВ избранном4
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Выделяют 3 типа Subjects, о каждом из которых мы сейчас поговорим

тащемто 4, про самый обычный Subject стоило упоминуть. И про разницу между ним и Бихейвор. Они самые часто используемные, потому это важно.

Спасибо большое! Все очень понятно изложено и примеры классные!!!

Дякую, не погана стаття!
Було би ще цікаво почитати про оператори :)

Подписаться на комментарии