Skip to the solution
HardFrontend10 min read

Explain the shareReplay operator in RxJS and how it prevents duplicate HTTP requests.

542 views

The problem

Question presented to candidate: "Our header, sidebar and profile panel all show the current user, and the network tab shows GET /api/me three times on every page load. Explain why, and how shareReplay fixes it. What does refCount change?"

What a strong answer should cover:

  • HttpClient Observables are cold: every subscribe() (including each async pipe) sends a new request.
  • shareReplay(n) multicasts: it subscribes to the source once, forwards values to all subscribers through an internal ReplaySubject, and replays the last n values to subscribers who arrive later.
  • share() alone dedupes concurrent subscribers but not late ones; shareReplay also covers the late ones.
  • refCount: with refCount: false (the shareReplay(1) default) the source subscription stays alive even when every subscriber leaves; with refCount: true it is torn down at zero subscribers. For HTTP this matters mostly for in-flight requests; for never-completing sources it is the difference between a leak and none.
  • Errors are not cached: a failed request is retried by the next subscriber. Also: where to keep the shared Observable (a root service field, not a method that returns a new pipe each call), and cache invalidation.

Clarifying questions expected:

  • "Should the cached value live for the whole session, or be refreshed (on navigation, after a time, after a mutation)?"
  • "Is the source a one-shot HTTP call or a long-lived stream?"

Code / implementation expected: Yes β€” a request counter with and without shareReplay, ideally showing a late subscriber.

  • rxjs
  • caching

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

10 min read Β· 10 sections

Read the code

AngularThree async pipes: three requests without sharing, one with shareReplay
import { AsyncPipe } from "@angular/common";
import { Component, signal } from "@angular/core";
import { Observable, shareReplay } from "rxjs";

interface User {
  name: string;
}

@Component({
  selector: "app-root",
  imports: [AsyncPipe],
  template: `
    <h4>Without sharing, requests made: {{ plainRequests() }}</h4>
    <span>header: {{ (plain$ | async)?.name }}</span>
    <span> sidebar: {{ (plain$ | async)?.name }}</span>
    <span> footer: {{ (plain$ | async)?.name }}</span>

    <h4>With shareReplay, requests made: {{ sharedRequests() }}</h4>
    <span>header: {{ (shared$ | async)?.name }}</span>
    <span> sidebar: {{ (shared$ | async)?.name }}</span>
    <span> footer: {{ (shared$ | async)?.name }}</span>

    <p><button (click)="showLate.set(true)">Open profile panel later</button></p>
    @if (showLate()) {
      <p>late plain: {{ (plain$ | async)?.name }} / late shared: {{ (shared$ | async)?.name }}</p>
    }
  `,
})
export class AppComponent {
  plainRequests = signal(0);
  sharedRequests = signal(0);
  showLate = signal(false);

  // Behaves like HttpClient.get: cold, one request per subscriber, one value, then complete.
  private fakeGet(counter: typeof this.plainRequests): Observable<User> {
    return new Observable<User>((subscriber) => {
      counter.update((n) => n + 1);
      const id = setTimeout(() => {
        subscriber.next({ name: "Ann" });
        subscriber.complete();
      }, 50);
      return () => clearTimeout(id);
    });
  }

  plain$ = this.fakeGet(this.plainRequests);
  shared$ = this.fakeGet(this.sharedRequests).pipe(shareReplay({ bufferSize: 1, refCount: true }));
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 99 of 121 in Angular

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Angular questions
Up nextHard

Deferrable views β€” prefetch strategies and dependency isolation with @defer?

Continue