All four are RxJS Subject variants, but they differ in what late subscribers receive and when values are emitted. Interviewers expect you to choose the right one based on initial value needs, replay history, completion behavior, and memory tradeoffs.
Subject vs BehaviorSubject vs ReplaySubject vs AsyncSubject in Angular: when do you use which?
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Core idea
These are all multicasting primitives, but they answer different product questions:
• Do I need an initial value right now?
• Should late subscribers see old values?
• Do I only care about the final value after completion?
Pick the wrong one and your UI will either miss state, replay too much history, or never emit when you expect.
Type | Initial value required? | What a late subscriber gets | Best fit |
|---|---|---|---|
| No | Only future emissions | Fire-and-forget events (click intents, toast triggers) |
| Yes | Latest value immediately | Current UI state (selected tab, auth status, filter value) |
| No | Last | History/buffer use-cases (chat stream, recent actions) |
| No | Final value only, and only after completion | One-shot workflows where only the final result matters |
import { Subject, BehaviorSubject, ReplaySubject, AsyncSubject } from 'rxjs';
// 1) Subject: event bus for "do something now" signals
const refreshClick$ = new Subject<void>();
// 2) BehaviorSubject: always has a current state value
const selectedTheme$ = new BehaviorSubject<'light' | 'dark'>('light');
// 3) ReplaySubject: keep last N values for late subscribers
const recentSearches$ = new ReplaySubject<string>(3);
// 4) AsyncSubject: emit only final value when complete() is called
const finalReport$ = new AsyncSubject<string>();
finalReport$.next('draft');
finalReport$.next('final');
finalReport$.complete(); // subscribers receive only 'final'
Scenario | Pick | Why |
|---|---|---|
Button click intent stream |
| No initial or replayed value needed |
Store current search/filter state |
| New subscribers need immediate current value |
Keep a short in-memory event history |
| Late subscribers need recent context |
Only publish final batch/job result |
| Intermediate values are noise |
import { Injectable } from '@angular/core';
import { BehaviorSubject, Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class SearchUiStore {
private readonly querySubject = new BehaviorSubject<string>('');
readonly query$ = this.querySubject.asObservable();
private readonly refreshSubject = new Subject<void>();
readonly refresh$ = this.refreshSubject.asObservable();
setQuery(value: string): void {
this.querySubject.next(value);
}
refresh(): void {
this.refreshSubject.next();
}
}
Common pitfall | Why it hurts | Fix |
|---|---|---|
Using | Consumers must handle meaningless initial states everywhere | Use |
Using large | Memory grows and old state lingers too long | Keep buffer small and intentional |
Choosing | No value is emitted because completion never happens | Use |
Exposing the Subject itself publicly | Any consumer can call | Keep Subject private and expose only |
Interview summarySubject = live events only. BehaviorSubject = current value + future updates. ReplaySubject = recent history + future updates. AsyncSubject = only final value on completion. In Angular, default to BehaviorSubject for local UI state, Subject for intent events, and ReplaySubject only when you truly need history.