angular
July 20

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

Теперь обе подписки получают одни и те же данные из одного источника. Это и есть эффект мультикастинга.