← Back to list

Stop Memorizing RxJS Operators!

If you’ve worked with Angular for even a few months, you’ve probably come across this interview question:

Rainbow Life · 2026-07-20 07:02 · 0 claps · 5.5 min read paywalled
#angular #rxjs #switchmap #mergemap #rxjs-operators
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Stop Memorizing RxJS Operators! Here’s How I Finally Understood switchMap, mergeMap, concatMap, and exhaustMap

AI Generated Image

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, and exhaustMap?"

The first time I was asked this, I answered with textbook definitions.

  • switchMap cancels previous observables.
  • mergeMap merges them.
  • concatMap queues them.
  • exhaustMap ignores 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