RxJS (Reactive Extensions for JavaScript), both Observable and Subject are used to manage asynchronous data streams, but they serve different purposes and use cases.

 

Observable: One-way, Unicast

  • When you subscribe to an Observable, you get a new execution for each subscriber.
  • Each subscriber is isolated — it gets its own stream.
  • Great for cold observables (like HTTP requests or timers).
  • Can't Emit Value No (producer emits)
  • Use Case: Async streams, cold data

 

const obs = new Observable(observer => {
 observer.next(Math.random());
});

obs.subscribe(val => console.log('A:', val));
obs.subscribe(val => console.log('B:', val));
// A and B get different random numbers

 

 

Subject: Multicast, Acts Like Both Observer and Observable

  • A Subject is an Observable that you can also manually push values into (it's also an Observer). 
  • When you emit a value, all subscribers get it — shared execution. 
  • Useful for event buses, cross-component communication, and when you want to imperatively push data.
  • Use case: Event bus, hot data

 

const subject = new Subject<number>();

subject.subscribe(val => console.log('A:', val));
subject.subscribe(val => console.log('B:', val));

subject.next(Math.random());
// A and B get the same value

 

 

Why You Need Subject

  • To share a single execution across multiple subscribers.
  • To manually control when and what data is emitted.
  • For scenarios like:
    • WebSocket messages
    • Manual event triggers
    • Centralized state or store

 

Rule of Thumb

  • Use Observable When
    • You're reacting to external data
    • The data source is cold (e.g., HTTP)
  • Use Subject When
    • You want multiple listeners to get same value at same time
    • You want control over emission
    • The data source is hot (e.g., WebSocket, user input)

 

 

Use Cases Observable

  • HTTP Request (cold data source)
    • Every subscription triggers a new request.
    • this.http.get('/api/user').subscribe(data => console.log('User:', data));
    • Each time you call it, you want a fresh result. You don’t want to share the same result across components unless you explicitly cache it.

 

  • Form Input ValueChanges
    • this.form.get('email')!.valueChanges.subscribe(value => {
            console.log('Email input changed:', value);
      });
    • You react to changes in a form control – each field has its own stream.

 

  • Route Parameters
    • this.route.params.subscribe(params => {
       console.log('Route changed to:', params['id']);
      });
    • Why Observable? Angular provides it to let you react whenever route parameters change.

 

 

Use Cases Subject 

  • Manual Refresh Button
    • A component triggers data reload by clicking a button:

refresh$ = new Subject<void>();

this.refresh$.pipe(
 switchMap(() => this.http.get('/api/data'))
).subscribe(data => console.log('Refreshed:', data));

// Called from button click
onRefresh() {
 this.refresh$.next();
}
Why Subject? You manually control when data should be reloaded.

  • Search Input Debounce

search$ = new Subject<string>();

ngOnInit() {
 this.search$.pipe(
   debounceTime(300),
   switchMap(query => this.searchService.query(query))
 ).subscribe(results => console.log(results));
}

onSearchChange(value: string) {
 this.search$.next(value);
}

Why Subject? You convert UI events (imperative) into a stream (reactive).

  • Global Event Bus
    Communicating between sibling components or services:

// EventBusService
event$ = new Subject<string>();

// Component A
this.eventBus.event$.subscribe(event => console.log('Received:', event));

// Component B
this.eventBus.event$.next('UserLoggedIn');
Why Subject? Broadcast events globally across unrelated components.

 



 

 


Related Question