The magic of the RxJS share operators

There are two main benefits of the RxJS share/shareReplay operators:

  1. Share() reduces repeated computations
    • With multiple subscribers to the same observable, you can process computations (side-effects) once, broadcast the results to whoever is listening.
  2. ShareReplay() = share() + making sure "everyone's on the same page"
    • When there are multiple subscribers, and you want to make sure they all get the same data, at the same time

1. Reduce Repeated Computations

Without share()

import { interval } from 'rxjs';
import { share, map } from 'rxjs/operators';
  
const source = interval(1000)
   .pipe(
         map((x: number) => {
             console.log('INTENSE computation for: ', x)
             return x * x;
         })
 );

source.subscribe(x => console.log('subscription 1: ', x));
source.subscribe(x => console.log('subscription 2: ', x));
 
// Console:
// INTENSE computation for:  0
// subscription 1:  0
// INTENSE computation for:  0
// subscription 2:  0
// INTENSE computation for:  1
// subscription 1:  1
// INTENSE computation for:  1
// subscription 2:  1
// INTENSE computation for:  2
// subscription 1:  4
// INTENSE computation for:  2
// subscription 2:  4

With share()

import { interval } from 'rxjs';
import { share, map } from 'rxjs/operators';
  
const source$ = interval(1000)
   .pipe(
         map((x: number) => {
             console.log('Heavy-duty computation for: ', x)
             return x * x;
         })

source$.subscribe(x => console.log('subscription 1: ', x));
source$.subscribe(x => console.log('subscription 2: ', x));
 );
// Console:
// INTENSE computation for:  0
// subscription 1:  0
// subscription 2:  0
// INTENSE computation for:  1
// subscription 1:  1
// subscription 2:  1
// INTENSE computation for:  2
// subscription 1:  4
// subscription 2:  4

2. Everyone's On the Same Page

import { Component } from "@angular/core";
import { Observable, of } from "rxjs";
import { delay, share, shareReplay, tap } from "rxjs/operators";

  @Component({
    selector: "my-app",
templateUrl: `
    <div *ngIf="(source$ | async)">
      <div *ngIf="(source$ | async)">
        <div>{{ source$ | async }}1</div>
      </div>
    </div>
    <div *ngIf="(source$ | async)">
      <div>{{ source$ | async }}2</div>
    </div>
  `,
})

export class AppComponent {
  source$: Observable<any>;

  ngOnInit() {
     this.source$ = this.getData();
 }

  getData() {
    // Fake Slow Async Data
    return of("here is data").pipe(
      tap(() => console.log("side-effect")),
      delay(2000)
    );
  }
}

If the yellow line wasthis.source$ = this.getData()Five calls to get data for five separate subscriptions, data displays asynchronously.

If the yellow line wasthis.source$ = this.getData(share())Three calls to get data, data displays asynchronously. Not so great if you're looking for more control over your display.

If the yellow line wasthis.source$ = this.getData(shareReplay(1))One call, everyone gets data all at once - everyone's on the same page.

Advanced Reading for the Technically Adventurous

  1. There is no need to use share or shareReplay for ngrx/store selectors because multicast is already enabled.
  2. share() is an alias for multicast(() => new Subject()),refCount()