Always Know When to Use Share vs. ShareReplay
Tags: rxjs, angular, javascript
Published: 2022-07-25 15:17:10

Using `share` and `shareReplay` is pretty darn confusing. The way `share` and `shareReplay` work is not always obvious and might lead to unexpected behavior in your application.

Fortunately, you have found this article and after reading you’ll understand the differences between `share` and `shareReplay`.

## share
The `share` operator will [multicast](https://en.wikipedia.org/wiki/Multicast) values emitted by a source Observable for subscribers.

Multicast means data is sent to multiple destinations.

As such, `share` allows you to avoid multiple executions of the source Observable when there are multiple subscriptions. `share` is particularly useful if you need to prevent repeated API calls or costly operations executed by Observables.

The slightly modified code from the [official documentation](https://rxjs.dev/api/operators/share) below has a shared source Observable that emits random numbers at 1-second intervals, up to two emissions. You can also run the example on [StackBlitz](https://stackblitz.com/edit/mpkssl?devToolsHeight=100&file=index.ts).

```typescript
import { interval, tap, map, take, share } from 'rxjs';

const source$ = interval(1000).pipe(
  tap((x) => console.log('Processing: ', x)),
  map(() => Math.round(Math.random() * 100)),
  take(2),
  // if you remove share, you will see that
  // each subscription will have its own execution of the source observable
  share()
);

source$.subscribe((x) => console.log('subscription 1: ', x));
source$.subscribe((x) => console.log('subscription 2: ', x));

setTimeout(
  // this subscription arrives late to the party. What will happen?
  () => source$.subscribe((x) => console.log('subscription 3: ', x)),
  1500
);

/* Example Run
### share operator logs:
--- 1 second
Processing: 0
subscription 1: 33
subscription 2: 33
--- 2 seconds
Processing: 1
subscription 1: 12
subscription 2: 12
subscription 3: 12

### without share operator logs:
--- 1 second
Processing: 0
subscription 1: 55
Processing: 0
subscription 2: 65
--- 2 seconds
Processing: 1
subscription 1: 64
Processing: 1
subscription 2: 2
--- 2.5 seconds
Processing: 0
subscription 3: 42
--- 3.5 seconds
Processing: 1
subscription 3: 95
*/
```

### share 's Inner Observable: Subject
When you subscribe to a shared Observable, you are [actually subscribing to a Subject](https://github.com/ReactiveX/rxjs/blob/7.5.5/src/internal/operators/share.ts#L192) exposed by the `share` operator. The `share` operator [also manages an inner Subscription to the source Observable](https://github.com/ReactiveX/rxjs/blob/7.5.5/src/internal/operators/share.ts#L235). The inner `Subject` is the reason multiple subscribers receive the same shared value, as they are receiving values from the `Subject` exposed by the `share` operator. See [previous example on StackBlitz](https://stackblitz.com/edit/mpkssl?devToolsHeight=100&file=index.ts).

### share's RefCount
`share` keeps a count of subscribers. Once the subscriber count reaches `0`, `share` will unsubscribe from the source Observable and reset its inner Observable (the `Subject`). The following (late) subscriber will trigger a new Subscription to the source Observable, or in other words, a new execution of the source Observable. Here’s an example of this behavior, [also available on StackBlitz](https://stackblitz.com/edit/uwwa2f?devToolsHeight=100&file=index.ts).

```typescript
import { defer, delay, of, share, shareReplay, tap } from 'rxjs';

const source$ = defer(() => of(Math.round(Math.random() * 100))).pipe(
  tap((x) => console.log('Processing: ', x)),
  delay(1000),
  // shareReplay({ bufferSize: 1, refCount: true }),
  share()
);

source$.subscribe((x) => console.log('subscription 1: ', x));
source$.subscribe((x) => console.log('subscription 2: ', x));

setTimeout(
  () => source$.subscribe((x) => console.log('subscription 3: ', x)),
  3500
);
```

## shareReplay
In some cases what you really need is a `share` that is able to behave as a [BehaviorSubject](https://rxjs.dev/api/index/class/BehaviorSubject) would. For example: if a cold Observable has a `share` operator, like the code example above, a late subscriber to it would never get the values emitted before the subscription because it subscribed after `share` operator reached `refCount` 0, which means the `share` operator unsubscribed from the source Observable and reset its inner Subject. The late subscriber would thus subscribe to a new inner Subject, which runs a new execution of the source Observable, in this case that means a second API call: exactly the opposite of what you really needed.

That's why `shareReplay` exists: it both shares the source Observable and replays the last emissions for late subscribers.

Also, it does not keep a count of subscribers by default, but you may use the refCount option with a `true` value to enable that behavior.

### shareReplay's Inner Observable: ReplaySubject
In contrast to `share`, `shareReplay` exposes a `ReplaySubject` to subscribers. `ReplaySubject(1)` is very [similar](https://rxjs.dev/api/index/class/ReplaySubject#differences-with-behaviorsubject) to a `BehaviorSubject`.

### shareReplay's RefCount
Since `shareReplay` does not keep track of a subscriber count by default, it is not able to unsubscribe to the source Observable. Ever. Unless you use the `refCount` option.

In order to use `shareReplay` while getting rid of memory leak issues you can use `bufferSize` and `refCount` options: `shareReplay({ bufferSize: 1, refCount: true })`.

`shareReplay` never resets its inner `ReplaySubject` when `refCount` reaches 0, but does unsubscribe from the source Observable. Late subscribers will not trigger a new execution of the source Observable and will receive up to `N (bufferSize)` emissions. [Play with the previous example on StackBlitz](https://stackblitz.com/edit/uwwa2f?devToolsHeight=100&file=index.ts) to see the difference.

## Use with Caution
In Angular, there are some gotchas when using `share` and `shareReplay`. Observables subscribed in the template with the `async` pipe might reach refCount `0` if unsubscribed automatically by the `async` pipe when inside a `*ngIf`, which would cause a new execution of the source Observable.

You might feel like the God of Time and Cache using `share` and `shareReplay`, but you should be aware that with great power comes great responsibility. If you want a partner to help you manage the high complexity of `share`, `shareReplay` and RxJS best practices, <a href="mailto:contact@bitovi.com">contact us today</a>. 🙂