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.
- this.form.get('email')!.valueChanges.subscribe(value => {
- 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.
- this.route.params.subscribe(params => {
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.