RxJS share() в Angular
Многие Observable в RxJS являются холодными. Это означает, что при каждой подписке создаётся новый поток, заново выполняются все операторы и возможные побочные эффекты — например, HTTP-запросы, таймеры, логирование и т.д.
Чтобы избежать повторных вычислений и разделить один источник данных между несколькими подписчиками, используется оператор share()
. Он превращает Observable в горячий и включает режим мультикастинга — подписчики получают данные из общего потока, а не инициируют новые.
Пример холодного Observable
Допустим, у нас есть сервис DataService
, который возвращает поток данных:
import { Injectable } from '@angular/core'; import { Observable, of, delay, tap } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DataService { private callCount = 0; // Холодный Observable — каждый вызов создаёт новый поток getData(): Observable<string> { this.callCount++; return of(`Ответ #${this.callCount}`).pipe( delay(1000), // имитируем задержку tap(value => console.log('🔥 Выполнен запрос!', value)) ); } }
И у нас есть компонент, который подписывается на этот Observable дважды:
import { Component, OnInit } from '@angular/core'; import { DataService } from '../../data.service'; @Component({ selector: 'app-cold', template: ` <h2>Cold Observable (без share)</h2> <div *ngIf="value1">Подписка 1: {{ value1 }}</div> <div *ngIf="value2">Подписка 2: {{ value2 }}</div> ` }) export class ColdComponent implements OnInit { value1?: string; value2?: string; constructor(private dataService: DataService) {} ngOnInit() { const obs$ = this.dataService.getData(); obs$.subscribe(value => (this.value1 = value)); obs$.subscribe(value => (this.value2 = value)); } }
Результат: в консоли мы увидим:
🔥 Выполнен запрос! Ответ #1 🔥 Выполнен запрос! Ответ #2
Каждая подписка вызывает Observable заново. Это может быть нежелательно, если Observable делает сетевой запрос или дорогостоящую операцию.
🔥 Преобразуем в горячий Observable с share()
Теперь давайте применим share()
и сделаем один общий поток:
import { Component, OnInit } from '@angular/core'; import { DataService } from '../../data.service'; import { share } from 'rxjs/operators'; @Component({ selector: 'app-shared', template: ` <h2>Shared Observable (с share)</h2> <div *ngIf="value1">Подписка 1: {{ value1 }}</div> <div *ngIf="value2">Подписка 2: {{ value2 }}</div> ` }) export class SharedComponent implements OnInit { value1?: string; value2?: string; constructor(private dataService: DataService) {} ngOnInit() { const shared$ = this.dataService.getData().pipe(share()); shared$.subscribe(value => (this.value1 = value)); shared$.subscribe(value => (this.value2 = value)); } }
🔥 Выполнен запрос! Ответ #3
Теперь обе подписки получают одни и те же данные из одного источника. Это и есть эффект мультикастинга.