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
Теперь обе подписки получают одни и те же данные из одного источника. Это и есть эффект мультикастинга.