Получение текущего значения RxJS Subject без подписки в Angular 2
Пройдите тест, узнайте какой профессии подходите
Быстрый ответ
Чтобы мгновенно получить последнее значение из BehaviorSubject
, употребите метод getValue()
. Обычные объекты типа Subject
не хранят значения внутри. Чтобы извлечь значение из Observable
, преобразуйте его в BehaviorSubject
, либо примените оператор take(1)
для получения одного излучаемого значения:
// BehaviorSubject
const behaviorSubject = new BehaviorSubject('initial');
console.log(behaviorSubject.getValue()); // 'initial' — это удалось
// Observable
observable.pipe(take(1)).subscribe(console.log); // выведет первое излучаемое значение — и на этом мы закончим
Используем BehaviorSubject и метод getValue
Особенность BehaviorSubject
заключается в том, что он сохраняет последнее значение и сразу же передает его новым подписчикам. Это идеально подходит для быстрого доступа к текущему состоянию при новой подписке.
// Создаем BehaviorSubject
const behaviorSubject = new BehaviorSubject('initial');
// Новая подписка тут же получает текущее значение
behaviorSubject.subscribe(value => console.log(value)); // 'initial', и можно приступать к работе
Метод getValue()
позволяет непосредственно извлечь текущее значение без создания новой подписки. Этот метод особенно полезен, если вам необходимы синхронные данные.
ReplaySubject и shareReplay: наши помощники во времени
ReplaySubject
может повторить ранее излучаемые значения для новых подписчиков. Оператор shareReplay()
позволяет Observable
работать в режиме мультикастинга, аналогично ReplaySubject
, предоставляя прошлые события подписчикам сразу после подписки.
// 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()
.
const source = interval(1000);
const connectableObservable = source.pipe(
publishReplay(1),
);
const subscription = connectableObservable.connect(); // И вот мы вышли на эфир!
Объединение Observables: сила синергии
Используйте withLatestFrom
или combineLatest
для объединения последних значений из нескольких Observables
. При каждом излучении одного из объектов мы получаем массив последних значений от каждого Observable
.
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()
, а затем дождаться получения значения в синхронном стиле. Однако такое превращение нам приходится тратить реактивность.
const currentValuePromise = observable.pipe(take(1)).toPromise();
const currentValue = await currentValuePromise; // И... поехали!
BehaviorSubject в роли менеджера состояния
В Angular часто используют объекты типа BehaviorSubject
для передачи данных о состоянии приложения. Выставляя эти состояния в виде Observable
, доступных только для чтения, мы обеспечиваем реактивный поток данных для компонентов. Главное правило — контролировать доступ к методу next()
внутри сервиса.
// 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()
помогает вам гарантировать излучение только значений, удовлетворяющих условиям, и завершает подписку после встречи с первым успешным событием.
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) позволяют подключаться к текущему эфиру:
const currentTune = subject.getValue(); // 🔊👂* Мы слушаем горячий хит от станции C
В чем особенность этих наушников? Они позволяют услышать текущую песню прямо сейчас, без необходимости ждать следующую.
Рекомендации по безопасности
Не стоит слишком увлекаться функцией getValue()
. Ее преимущества могут помешать вам придерживаться реактивных принципов. Стремитесь поддерживать реактивные потоки данных, чтобы приложение было более понятным и, следовательно, более подконтрольным.
Не забываем про отписку при использовании объектов типа BehaviorSubject
и ReplaySubject
для предотвращения потенциальных утечек памяти. Для элегантного контроля подписок используйте метод pipe
в компании с оператором takeUntil
.
Мастерство работы с мультикастингом
Для более тонкого управления мультикастингом попробуйте комбинацию RefCount
c publishReplay()
или publishBehavior()
. Такой подход гарантирует, что обмен данными начинается только тогда, когда есть хотя бы один подписчик, и прекращается при их полном отсутствии.
Раздумья об архитектуре
Выбор между объектами типа Subjects
и Observables
будет определять реактивную архитектуру вашего приложения. Отражает ли она реальную динамику взаимодействия между компонентами, между источниками и потребителями данных?
Полезные материалы
- GitHub – ReactiveX/rxjs – Исходный код RxJS для изучения и внесения вклада в библиотеку.
- RxJS Официальная документация – Руководство по Subjects, включая
BehaviorSubject
. - ReactiveX – Операторы – Детальное описание разнообразных операторов RxJS.
- Stack Overflow – Разница между BehaviorSubject и Observable – Обсуждение различий между
BehaviorSubject
иObservable
. - Hot vs Cold Observables – автор Ben Lesh | Medium – Статья Бена Леша о горячих и холодных объектах Observable.
- BehaviorSubject – Учим RxJS – Руководство и примеры использования объектов типа
BehaviorSubject
. - Понимание Behavior, Replay и Async Subject в RxJS – YouTube – Видеообзор, посвященный особенностям
BehaviorSubject
,ReplaySubject
иAsyncSubject
.