Stop Memorizing RxJS Operators!
If you’ve worked with Angular for even a few months, you’ve probably come across this interview question:
Stop Memorizing RxJS Operators! Here’s How I Finally Understood switchMap, mergeMap, concatMap, and exhaustMap

AI Generated Image
If you’ve worked with Angular for even a few months, you’ve probably come across this interview question:
“What’s the difference between
switchMap,mergeMap,concatMap, andexhaustMap?"
The first time I was asked this, I answered with textbook definitions.
switchMapcancels previous observables.mergeMapmerges them.concatMapqueues them.exhaustMapignores new ones.
Technically correct.
But if you’ve ever stared at your code wondering “Which one should I actually use here?”, those definitions don’t help much.
The breakthrough for me came when I stopped thinking about what these operators do and started thinking about how they behave when multiple events happen quickly.
Let’s dive in.
The One Question That Solves Everything
Imagine you’re building a search box.
The user types:
A
An
Ang
Angu
Angul
Angular
Each keystroke triggers an HTTP request.
A ---------> Request
An --------> Request
Ang -------> Request
Angu ------> Request
Angular ---> Request
Now ask yourself one simple question:
What should happen to the previous request when a new value arrives?
Your answer determines which RxJS operator you should use.
Let’s see how each operator behaves.
1. switchMap — “Forget the Past”
Think of switchMap as someone with a short attention span.
The moment something new arrives…
It completely forgets what it was doing.
A ----------X
An ---------X
Ang --------X
Angular ---------------- Complete
Every previous observable is cancelled.
Only the latest one survives.
Angular Example
this.searchControl.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(term =>
this.http.get<SearchResult[]>(`/api/search?q=${term}`)
)
).subscribe(results => {
this.results = results;
});
Suppose the user types very quickly.
A
An
Ang
Angular
Without switchMap, four HTTP requests would race to complete.
Imagine this timeline:
Angular -> 200ms
Ang ----> 500ms
An -----> 700ms
A ------> 900ms
The user first sees:
Angular Results
Then suddenly…
Ang Results
Then…
An Results
Finally…
A Results
Your UI now shows completely outdated data.
This is exactly why switchMap exists.
It cancels all previous requests so only the latest response updates the UI.
Perfect Use Cases
✅ Search Autocomplete
✅ Live Search
✅ Filter Panels
✅ Route Parameter Changes
✅ Search-as-you-type
2. mergeMap — “Everyone Gets a Chance”
Unlike switchMap, mergeMap is very generous.
It lets every request execute.
A --------
An --------
Ang --------
Angular --------
Nothing gets cancelled.
Everything runs simultaneously.
Angular Example
Imagine uploading multiple files.
from(files).pipe(
mergeMap(file =>
this.http.post('/upload', file)
)
).subscribe();
Suppose the user uploads:
photo1.jpg
photo2.jpg
photo3.jpg
photo4.jpg
Would you really want them to upload one after another?
Of course not.
Uploading all four together is much faster.
That’s exactly what mergeMap does.
The Catch
The order isn’t guaranteed.
Input:
1
2
3
Completion:
2
3
1
Output:
2
3
1
If order matters…
mergeMap is the wrong choice.
Great Use Cases
- File uploads
- Sending notifications
- Independent API calls
- Background processing
- Analytics events
3. concatMap — “Stand in Line”
Imagine waiting at a bank.
One customer is served at a time.
Everyone else waits.
That’s exactly how concatMap works.
Request A ---------- Complete
Request B ---------- Complete
Request C ---------- Complete
No overlap.
Everything executes sequentially.
Angular Example
Suppose users submit multiple purchase orders.
from(orders).pipe(
concatMap(order =>
this.http.post('/orders', order)
)
).subscribe();
Orders arrive like this:
Order A
Order B
Order C
Processing becomes:
A
↓
B
↓
C
Always.
No matter how fast the requests complete.
Why This Matters
Imagine inventory updates.
Current Stock = 10
Order A buys 2
Order B buys 4
If both requests execute simultaneously, race conditions can occur.
Processing them sequentially keeps everything predictable.
Perfect Use Cases
- Payment processing
- Order creation
- Database updates
- Sequential workflows
- Printing queues
4. exhaustMap — “I’m Busy Right Now”
This one confused me the most when I first learned RxJS.
The easiest way to remember it is this:
If I’m already busy, ignore everyone else.
Click
Click
Click
Click
↓
Only the first click executes.
Angular Example
fromEvent(submitButton, 'click').pipe(
exhaustMap(() =>
this.http.post('/api/login', credentials)
)
).subscribe();
Imagine an impatient user.
Click
Click
Click
Click
Without protection…
Your backend receives:
Login Request
Login Request
Login Request
Login Request
With exhaustMap...
Only the first request is processed.
Everything else is ignored until it completes.
Perfect Use Cases
- Login buttons
- Checkout
- Payment
- Registration forms
- Preventing duplicate submissions
What Happens If You Choose the Wrong Operator?
Using mergeMap ❌
Every request is sent.
A
An
Ang
Angular
Now imagine the network responds in this order:
Angular
Ang
An
A
The UI first displays the correct results for “Angular”.
But a slower request for “A” finishes later and overwrites the latest results.
Congratulations — you’ve just introduced a race condition.
A race condition occurs when multiple asynchronous operations compete to update the same piece of data, and whichever finishes last wins — even if it’s no longer the correct result.
The result is stale data being displayed to the user.
Instead of seeing results for what they actually typed, they’re looking at outdated search results.
This is exactly why switchMap is the preferred choice for search autocomplete. It automatically cancels obsolete requests, preventing race conditions and ensuring that stale data never reaches your UI.
Using concatMap ❌
Requests execute one after another.
A
↓
An
↓
Ang
↓
Angular
If each request takes 500 ms, the user waits around two seconds before seeing results for the final search term.
The UI eventually displays the correct results, but the experience feels sluggish because outdated requests block the latest one.
Using exhaustMap ❌
The first request starts.
Every other search is ignored until it finishes.
User Types
A
An
Ang
Angular
Only “A” is searched.
The user ends up seeing results that don’t match what’s currently in the search box.
This is why exhaustMap is not suitable for search autocomplete.
However, it’s perfect for preventing duplicate submissions.
Imagine a user clicking the Login or Place Order button multiple times because the application feels slow.
Without protection:
POST /login
POST /login
POST /login
POST /login
or
POST /checkout
POST /checkout
POST /checkout
The server receives multiple identical requests, potentially creating duplicate orders, charging the customer multiple times, or generating inconsistent data.
Using exhaustMap ensures that once the first request starts, every subsequent click is ignored until the request completes. This effectively prevents duplicate submissions and protects both the user experience and your backend from unintended repeated actions.
Visual Comparison
switchMap
A --------X
B --------X
C ---------------- Done
Cancels previous.
mergeMap
A ----------------
B ----------------
C ----------------
Runs everything.
concatMap
A----------------
B----------------
C----------------
Queues requests.
exhaustMap
A----------------
B ignored
C ignored
D ignored
Ignores new values while busy.
The Interview Question Everyone Asks
Which operator would you use for Search Autocomplete?
The answer is:
✅ switchMap
Here’s why.
The user types:
A
An
Ang
Angular
The only request that matters is:
Angular
Every previous request is already outdated.
switchMap cancels them automatically.
This saves bandwidth, improves performance, and prevents stale responses from overwriting the latest search results.
What Happens If You Choose the Wrong Operator?
Let’s see what can go wrong.
Using mergeMap ❌
Every request is sent.
A
An
Ang
Angular
Network responses arrive in this order:
Angular
Ang
An
A
Your UI first shows the correct Angular results…
Then suddenly switches to results for “A”.
This is a classic race condition.
Using concatMap ❌
Requests execute one after another.
A
↓
An
↓
Ang
↓
Angular
If each request takes 500 ms, the user waits around 2 seconds before seeing the final results.
The search feels slow and unresponsive.
Using exhaustMap ❌
The first request starts.
Every other search is ignored until it finishes.
User Types
A
An
Ang
Angular
Only “A” is searched.
The user ends up seeing results that don’t match what’s currently typed in the input field.
A Simple Way to Remember Them Forever
Whenever a new value arrives, ask yourself:
Do I only care about the latest value?
Use **switchMap**.
Should every request execute?
Use **mergeMap**.
Does order matter?
Use **concatMap**.
Should duplicate requests be ignored while one is running?
Use **exhaustMap**.
Checkout for more details : https://youtu.be/U2cO1Q4Fono
Final Thoughts
One of the biggest mistakes developers make is trying to memorize RxJS operators.
Instead, ask a single question:
“What should happen if another value arrives before the current work is finished?”
If you can answer that, choosing between switchMap, mergeMap, concatMap, and exhaustMap becomes surprisingly easy.
That’s exactly how I approach it today — whether I’m reviewing code, building enterprise Angular applications, or answering interview questions.
Once you stop memorizing and start thinking in terms of user interactions and concurrency, RxJS begins to feel much more intuitive.
[embed]
메타데이터
- post_id
- 0f0c8ad7ff03
- slug
- stop-memorizing-rxjs-operators-0f0c8ad7ff03
- url
- https://medium.com/@anumathew16/stop-memorizing-rxjs-operators-0f0c8ad7ff03
- canonical_url
- https://medium.com/@anumathew16/stop-memorizing-rxjs-operators-0f0c8ad7ff03
- author_url
- https://medium.com/@anumathew16
- status
- ok
- fetched_at
- 2026-09-04 21:40:00