RxJS From A to Z — Part 2: Hot vs Cold Observables, Schedulers, Practical Use Cases
L. Cold vs Hot Observables
Understanding this difference is crucial to know how an Observable "creates" data:
📌 Cold Observable
- The data stream **starts with each subscriber**
- Data is produced internally (lazy)
import { Observable } from 'rxjs';
const cold$ = new Observable(observer => {
console.log('Observable started');
observer.next(Math.random());
});
cold$.subscribe(val => console.log('Sub A:', val));
cold$.subscribe(val => console.log('Sub B:', val));
Result: Different random numbers
🔥 Hot Observable
- The data stream **starts independently of subscribers**
- Used with: Subject, WebSocket, DOM Event, etc.
import { Subject } from 'rxjs';
const hot$ = new Subject();
hot$.subscribe(val => console.log('Sub A:', val));
hot$.next(Math.random());
hot$.subscribe(val => console.log('Sub B:', val));
hot$.next(Math.random());
Result: Sub B won't receive the first value because it subscribed late.
M. RxJS Schedulers (Manage async/processing timing)
Schedulers allow controlling when and how an Observable performs emit/subscribe operations.
asyncScheduler– like setTimeoutqueueScheduler– synchronous, in orderasapScheduler– Promise microtask
import { of, asyncScheduler } from 'rxjs'; console.log('Before'); of(1, 2, 3, asyncScheduler).subscribe(val => console.log(val)); console.log('After'); Output:
- Before
- After
- 1
- 2
- 3
Meaning: asyncScheduler executes in the next phase (like setTimeout)
N. Real-World Use Case 1: Live Search (Autocomplete)
fromEvent(searchInput, 'input') .pipe( map(e => e.target.value), debounceTime(300), distinctUntilChanged(), switchMap(query => http.get('/api/search?q=' + query)) ) .subscribe(results => renderResults(results)); - debounceTime – waits until typing stops
- distinctUntilChanged – doesn't send requests with the same value
- switchMap – sends new request, canceling the old one
O. Real-World Use Case 2: Button Click Prevent Spamming
fromEvent(button, 'click') .pipe( throttleTime(2000) ) .subscribe(() => { // Call API or do action console.log('Clicked!'); }); Result: After clicking, it will only allow another click after 2 seconds.
P. Combining Observables — combineLatest, forkJoin, zip
combineLatest– returns latest values when any observable emitsforkJoin– waits until all completezip– combines streams in order
combineLatest([ of(1), interval(1000).pipe(take(3)) ]).subscribe(console.log);