Skip to the solution
MediumFrontend10 min read

What is the async pipe and why is it useful?

758 views

The problem

Question presented to candidate: "In this template we use user$ | async in three places and someone says that is fine because the async pipe handles everything. Explain what the async pipe actually does, why it is useful, and whether that template is fine."

What a strong answer should cover:

  • | async subscribes to an Observable (or waits on a Promise), returns the latest value (null before the first one), and calls markForCheck() on each new value, so it works with OnPush and zoneless apps.
  • It unsubscribes automatically when the view is destroyed, and when the expression starts returning a different Observable instance.
  • Each | async is its own subscription: three pipes on a cold HTTP Observable are three requests. Use one @if (user$ | async; as user) block, shareReplay, or toSignal().
  • Never pipe a method or getter that creates a new Observable on every evaluation: it re-subscribes forever and never shows a value.
  • Errors from the stream go to the app ErrorHandler; handle them with catchError in the pipeline. The pipe does not unwrap signals.
  • In Angular 22, toSignal() is the signal-first alternative; the async pipe remains fully supported.

Clarifying questions expected:

  • "Is user$ an HTTP call or a shared store stream?"
  • "Is this component signal-based already, or still Observable-based?"

Code / implementation expected: Yes — one async-pipe subscription with @if ... as, a live stream showing unsubscribe on destroy, and a Promise.

  • async-pipe
  • rxjs

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

Angularasync pipe with @if as, a live stream, a Promise, and unsubscribe on destroy
import { Component, signal } from "@angular/core";
import { AsyncPipe } from "@angular/common";
import { Observable, interval, map } from "rxjs";

// Counts live subscriptions so we can watch the async pipe subscribe and unsubscribe.
export const live = signal(0);
export const requests = signal(0);

function fakeHttp<T>(value: T, ms = 60): Observable<T> {
  return new Observable<T>((sub) => {
    requests.update((n) => n + 1);
    const id = setTimeout(() => { sub.next(value); sub.complete(); }, ms);
    return () => clearTimeout(id);
  });
}

function liveTicks(): Observable<number> {
  return new Observable<number>((sub) => {
    live.update((n) => n + 1);
    const inner = interval(50).pipe(map((i) => i + 1)).subscribe(sub);
    return () => { inner.unsubscribe(); live.update((n) => n - 1); };
  });
}

@Component({
  selector: "app-profile",
  imports: [AsyncPipe],
  template: `
    <!-- one subscription, a loading branch, a typed local variable -->
    @if (user$ | async; as user) { <p>{{ user.name }} ({{ user.role }})</p> } @else { <p>Loading...</p> }
    <p>ticks: {{ ticks$ | async }}</p>
    <p>promise: {{ greeting | async }}</p>
  `,
})
export class ProfileComponent {
  user$ = fakeHttp({ name: "Ada", role: "admin" });
  ticks$ = liveTicks();
  greeting = new Promise<string>((resolve) => setTimeout(() => resolve("hello"), 30));
}

@Component({
  selector: "app-root",
  imports: [ProfileComponent],
  template: `
    <button (click)="show.update(s => !s)">{{ show() ? "Hide" : "Show" }} profile</button>
    @if (show()) { <app-profile /> }
    <p>HTTP requests: {{ requests() }} | live tick subscriptions: {{ live() }}</p>
  `,
})
export class AppComponent {
  show = signal(true);
  requests = requests;
  live = live;
}

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 36 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 nextMedium

Standalone bootstrapApplication — how does Angular bootstrap without NgModules in 2025?

Continue