Получение текущего значения RxJS Subject без подписки в Angular 2

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Чтобы мгновенно получить последнее значение из BehaviorSubject, употребите метод getValue(). Обычные объекты типа Subject не хранят значения внутри. Чтобы извлечь значение из Observable, преобразуйте его в BehaviorSubject, либо примените оператор take(1) для получения одного излучаемого значения:

JS
Скопировать код
// BehaviorSubject
const behaviorSubject = new BehaviorSubject('initial');
console.log(behaviorSubject.getValue()); // 'initial' — это удалось

// Observable
observable.pipe(take(1)).subscribe(console.log); // выведет первое излучаемое значение — и на этом мы закончим
Кинга Идем в IT: пошаговый план для смены профессии

Используем BehaviorSubject и метод getValue

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

JS
Скопировать код
// Создаем BehaviorSubject
const behaviorSubject = new BehaviorSubject('initial');

// Новая подписка тут же получает текущее значение
behaviorSubject.subscribe(value => console.log(value)); // 'initial', и можно приступать к работе

Метод getValue() позволяет непосредственно извлечь текущее значение без создания новой подписки. Этот метод особенно полезен, если вам необходимы синхронные данные.

ReplaySubject и shareReplay: наши помощники во времени

ReplaySubject может повторить ранее излучаемые значения для новых подписчиков. Оператор shareReplay() позволяет Observable работать в режиме мультикастинга, аналогично ReplaySubject, предоставляя прошлые события подписчикам сразу после подписки.

JS
Скопировать код
// ReplaySubject
const replaySubject = new ReplaySubject(3);
replaySubject.next(1);
replaySubject.next(2);
replaySubject.next(3);

replaySubject.subscribe(value => console.log(value)); // Выдаст 1, 2, 3... знакомые числа?

// shareReplay сместе с Observable
const sharedObservable = sourceObservable.pipe(shareReplay(1)); // Поделиться значит заботиться о других

Мультикастинг с использованием Subjects: согрейтесь!

Операторы publishReplay(), publishBehavior() и multicast() могут превратить холодные Observable в горячие мультисообщения и поделиться их теплом с несколькими подписчиками. Для запуска совместной трансляции следует использовать метод connect().

JS
Скопировать код
const source = interval(1000);
const connectableObservable = source.pipe(
    publishReplay(1),
);

const subscription = connectableObservable.connect(); // И вот мы вышли на эфир!

Объединение Observables: сила синергии

Используйте withLatestFrom или combineLatest для объединения последних значений из нескольких Observables. При каждом излучении одного из объектов мы получаем массив последних значений от каждого Observable.

JS
Скопировать код
const priceTicker$ = new BehaviorSubject(100);
const quantityInput$ = new BehaviorSubject(1);

const totalCost$ = priceTicker$.pipe(
  withLatestFrom(quantityInput$),
  map(([price, quantity]) => price * quantity),
);

totalCost$.subscribe(console.log);  // Стоимость всего содержимого... возьмите ваш заказ, пожалуйста

Превращение текущего значения в промис

Иногда требуется получить текущее значение, словно подавая его на блюдечке. В таком случае Observable можно преобразовать в Promise с помощью метода toPromise(), а затем дождаться получения значения в синхронном стиле. Однако такое превращение нам приходится тратить реактивность.

JS
Скопировать код
const currentValuePromise = observable.pipe(take(1)).toPromise();
const currentValue = await currentValuePromise; // И... поехали!

BehaviorSubject в роли менеджера состояния

В Angular часто используют объекты типа BehaviorSubject для передачи данных о состоянии приложения. Выставляя эти состояния в виде Observable, доступных только для чтения, мы обеспечиваем реактивный поток данных для компонентов. Главное правило — контролировать доступ к методу next() внутри сервиса.

JS
Скопировать код
// service.ts
private state = new BehaviorSubject<MyState>(initialState);
state$ = this.state.asObservable();

setState(newState: MyState): void {
  this.state.next(newState); // Мы можем менять состояния также просто, как перелистываем страницы
}

// component.ts
this.service.state$.subscribe(state => console.log(state)); // Ждем актуальных данных

Фильтр и First — ваша защита от лишнего

Использование filter() вместе с first() помогает вам гарантировать излучение только значений, удовлетворяющих условиям, и завершает подписку после встречи с первым успешным событием.

JS
Скопировать код
observable.pipe(
  filter(value => value != null), // Только значимые данные
  first(), // Единственное событие — и исчезаем!
).subscribe(console.log); // Вот и условное значение!

Практические советы

  • Избавьтесь от привычки часто использовать метод getValue(), чтобы оставаться верным идеям реактивного программирования RxJS.
  • Используйте BehaviorSubject и ReplaySubject наиболее эффективно в соответствии с требованиями вашего приложения.
  • Грамотный выбор типа Subject в RxJS и подходящего оператора может значительно усовершенствовать архитектуру вашего приложения.
  • Отдавайте предпочтение combineLatest и withLatestFrom для совместной передачи данных вместо громоздких цепочек вызовов.
  • Обновляйте BehaviorSubject с помощью метода next() в сервисах для отражения изменений в реальном времени у всех подписчиков.

Визуализация

Представьте себе радио (📻) с большим числом закодированных станций (Observables): | Радиостанция (Observable) | 📻 | | ------------------ | --- | | Станция А | 🎵 | | Станция B | 🎶 | | Станция C | 🔊* | | Станция D | 🎼 |

Как же узнать, какую песню сейчас играют? Специальные наушники (.getValue() для Subject) позволяют подключаться к текущему эфиру:

JS
Скопировать код
const currentTune = subject.getValue(); // 🔊👂* Мы слушаем горячий хит от станции C

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

Рекомендации по безопасности

Не стоит слишком увлекаться функцией getValue(). Ее преимущества могут помешать вам придерживаться реактивных принципов. Стремитесь поддерживать реактивные потоки данных, чтобы приложение было более понятным и, следовательно, более подконтрольным.

Не забываем про отписку при использовании объектов типа BehaviorSubject и ReplaySubject для предотвращения потенциальных утечек памяти. Для элегантного контроля подписок используйте метод pipe в компании с оператором takeUntil.

Мастерство работы с мультикастингом

Для более тонкого управления мультикастингом попробуйте комбинацию RefCount c publishReplay() или publishBehavior(). Такой подход гарантирует, что обмен данными начинается только тогда, когда есть хотя бы один подписчик, и прекращается при их полном отсутствии.

Раздумья об архитектуре

Выбор между объектами типа Subjects и Observables будет определять реактивную архитектуру вашего приложения. Отражает ли она реальную динамику взаимодействия между компонентами, между источниками и потребителями данных?

Полезные материалы

  1. GitHub – ReactiveX/rxjs – Исходный код RxJS для изучения и внесения вклада в библиотеку.
  2. RxJS Официальная документация – Руководство по Subjects, включая BehaviorSubject.
  3. ReactiveX – Операторы – Детальное описание разнообразных операторов RxJS.
  4. Stack Overflow – Разница между BehaviorSubject и Observable – Обсуждение различий между BehaviorSubject и Observable.
  5. Hot vs Cold Observables – автор Ben Lesh | Medium – Статья Бена Леша о горячих и холодных объектах Observable.
  6. BehaviorSubject – Учим RxJS – Руководство и примеры использования объектов типа BehaviorSubject.
  7. Понимание Behavior, Replay и Async Subject в RxJS – YouTube – Видеообзор, посвященный особенностям BehaviorSubject, ReplaySubject и AsyncSubject.