Day 14 — Advanced RxJS: switchMap, mergeMap, forkJoin & CatchError
📘 Day 14 — Advanced RxJS: switchMap, mergeMap, forkJoin & CatchError
Zero to Hero — Hands-on Angular Tutorial
Today you will learn:
- ✔️ The concept of Higher-Order Mapping (Flattening)
- ✔️ switchMap (The cancel & switch operator)
- ✔️ mergeMap (The parallel/fire-hose operator)
- ✔️ concatMap (The queue/serial operator)
- ✔️ forkJoin (Waiting for multiple APIs)
- ✔️ catchError (Handling stream failures)
This is the hardest part of RxJS. Once you understand this, you are in the top 10% of Angular developers. 🧠
🟦 1. The Problem: Nested Subscriptions (Callback Hell)
Imagine you need to:
- Get a User ID.
- Use that ID to fetch User Details.
Bad Way (Nested Subscriptions):
this.route.params.subscribe(params => {
const id = params['id'];
// ⚠️ TRAP: Nested Subscribe! Hard to unsubscribe, leaky.
this.http.get(`/api/users/${id}`).subscribe(user => {
console.log(user);
});
});
Good Way (Flattening Operators): Used to “map” one Observable into another.
🟩 2. switchMap (The “Latest & Greatest”)
Behavior: When a new value arrives, cancel the previous inner observable and start the new one.
Use Case: Typeahead Search. If I type “Ang”, request starts. If I quickly type “Angu”, cancel “Ang” request (save bandwidth) and search “Angu”.
import { switchMap } from 'rxjs/operators';
searchField.valueChanges.pipe(
debounceTime(300),
// If user types again, CANCEL previous HTTP call
switchMap(term => this.http.get(`/api/search?q=${term}`))
).subscribe(results => {
console.log('Search results:', results);
});
🟧 3. mergeMap (The “Fire Hose”)
Behavior: Run everything in parallel. Don’t cancel anything.
Use Case: Deleting multiple items. If I click “Delete” on 5 items quickly, I want ALL 5 delete requests to go to the server. Do not cancel any.
import { mergeMap } from 'rxjs/operators';
import { from } from 'rxjs';
const idsToDelete = [1, 2, 3];
from(idsToDelete).pipe(
// Send all requests at once!
mergeMap(id => this.http.delete(`/api/users/${id}`))
).subscribe(res => console.log('Deleted one item'));
🟥 4. concatMap (The “Queue”)
Behavior: Wait for the current one to finish before starting the next. (Sequential).
Use Case: Saving data where order matters. (e.g., Step 1 must finish before Step 2 starts).
import { concatMap } from 'rxjs/operators';
// Imagine saving a form in precise steps
const steps$ = from(['Step 1', 'Step 2', 'Step 3']);
steps$.pipe(
concatMap(step => this.saveStepApi(step))
).subscribe(res => console.log('Step completed in order!'));
🟫 5. forkJoin (Promise.all for Observables)
Behavior: Wait for ALL provided observables to complete, then give me the final results as an array.
Use Case: Loading a Dashboard. You need Profile, Notifications, and Settings before showing the page.
import { forkJoin } from 'rxjs';
forkJoin({
profile: this.http.get('/api/profile'),
notifications: this.http.get('/api/notifications'),
settings: this.http.get('/api/settings')
}).subscribe({
next: (result) => {
console.log(result.profile);
console.log(result.notifications);
console.log(result.settings);
// Render UI now...
}
});
🟨 6. Robust Error Handling
If an Observable errors, it dies (stops listening).
To keep it alive (e.g., in a Search bar), catch the error inside the pipe.
import { catchError, of, EMPTY } from 'rxjs';
source$.pipe(
switchMap(term =>
this.http.get(url).pipe(
// 🛡️ Catch error HERE (Inner Observable)
// Return 'EMPTY' (ignore) or fallback data 'of([])'
catchError(err => {
console.error('API Failed', err);
return of([]); // Return empty results to keep app running
})
)
)
).subscribe(); // The main subscription stays alive!
🎉 End of Day 14 — What You Learned
Today you mastered the “Big 4” of RxJS/Angular Mapping:
- ✔️
switchMap: Cancel previous (Search). - ✔️
mergeMap: Parallel execution (Deletes/Writes). - ✔️
concatMap: Sequential execution (Order sensitive). - ✔️
forkJoin: Join multiple sources (Initial Load). - ✔️
catchError: Prevent crashes.
You are now capable of handling complex async logic that Signals alone cannot easily do.
🧪 Day 14 Challenge
Build a “Dashboard Loader” Service.
Requirements:
- Mock 3 API calls (Users, Posts, Comments) using
timer()orof().pipe(delay()). - Use
forkJointo wait for all 3. - Display “Loading…” while waiting.
- Display the combined data when finished.
- Bonus: Make one API fail, and handle it gracefully using
catchErrorso the other 2 still show up.