Share source and replay specified number of emissions on subscription.
Why use shareReplay?
You generally want to use shareReplay when you have side-effects or taxing computations that you do not wish to be executed amongst multiple subscribers. It may also be valuable in situations where you know you will have late subscribers to a stream that need access to previously emitted values. This ability to replay values on subscription is what differentiates share and shareReplay.
For instance, suppose you have an observable that emits the last visited url. In the first example we are going to use share:
// simulate url change with subjectconstrouteEnd=newSubject<{data:any, url:string}>();// grab url and share with subscribersconstlastUrl=routeEnd.pipe(pluck('url'),share());// initial subscriber requiredconstinitialSubscriber=lastUrl.subscribe(console.log);// simulate route changerouteEnd.next({data: {}, url:'my-path'});// nothing loggedconstlateSubscriber=lastUrl.subscribe(console.log);
In the above example nothing is logged as the lateSubscriber subscribes to the source. Now suppose instead we wanted to give access to the last emitted value on subscription, we can accomplish this with shareReplay:
import { Subject } from'rxjs/Subject';import { ReplaySubject } from'rxjs/ReplaySubject';import { pluck, share, shareReplay, tap } from'rxjs/operators';// simulate url change with subjectconstrouteEnd=newSubject<{data:any, url:string}>();// grab url and share with subscribersconstlastUrl=routeEnd.pipe(tap(_ =>console.log('executed')),pluck('url'),// defaults to all values so we set it to just keep and replay last oneshareReplay(1));// requires initial subscriptionconstinitialSubscriber=lastUrl.subscribe(console.log);// simulate route change// logged: 'executed', 'my-path'routeEnd.next({data: {}, url:'my-path'});// logged: 'my-path'constlateSubscriber=lastUrl.subscribe(console.log);
Note that this is similar behavior to what you would see if you subscribed a ReplaySubject to the lastUrl stream, then subscribed to that Subject:
// simulate url change with subjectconstrouteEnd=newSubject<{data:any, url:string}>();// instead of using shareReplay, use ReplaySubjectconstshareWithReplay=newReplaySubject();// grab url and share with subscribersconstlastUrl=routeEnd.pipe(pluck('url')).subscribe(val =>shareWithReplay.next(val));// simulate route changerouteEnd.next({data: {}, url:'my-path'});// subscribe to ReplaySubject instead// logged: 'my path'shareWithReplay.subscribe(console.log);
In fact, if we dig into the source code we can see a very similar technique is being used. When a subscription is made, shareReplay will subscribe to the source, sending values through an internal ReplaySubject:
// RxJS v6+import { Subject, ReplaySubject } from'rxjs';import { pluck, share, shareReplay, tap } from'rxjs/operators';// simulate url change with subjectconstrouteEnd=newSubject<{data:any, url:string}>();// grab url and share with subscribersconstlastUrl=routeEnd.pipe(tap(_ =>console.log('executed')),pluck('url'),// defaults to all values so we set it to just keep and replay last oneshareReplay(1));// requires initial subscriptionconstinitialSubscriber=lastUrl.subscribe(console.log)// simulate route change// logged: 'executed', 'my-path'routeEnd.next({data: {}, url:'my-path'});// logged: 'my-path'constlateSubscriber=lastUrl.subscribe(console.log);