Discover how these three operators can save your app from chaos when handling many tasks at once!
mergeMap vs concatMap vs exhaustMap in Angular - When to Use Which
Imagine you have multiple tasks that start one after another, like sending several requests to a server when a user clicks buttons quickly.
You try to handle each request manually, starting a new one every time without waiting for the previous to finish.
Manually managing these overlapping tasks is confusing and error-prone.
You might send too many requests at once, causing overload or unexpected results.
Or you might accidentally ignore some tasks or run them in the wrong order.
Using mergeMap, concatMap, and exhaustMap operators in Angular helps control how tasks run and overlap.
They let you decide if tasks run all at once, one after another, or ignore new tasks while busy.
This makes your code simpler, safer, and easier to understand.
buttonClicks.forEach(() => { sendRequest(); });buttonClicks.pipe(mergeMap(() => sendRequest()));
They enable smooth, predictable handling of multiple asynchronous tasks, improving app performance and user experience.
When a user clicks a search button multiple times quickly, concatMap queues the requests so they run one by one, avoiding server overload.
mergeMap runs all tasks simultaneously.
concatMap runs tasks one after another, preserving order.
exhaustMap ignores new tasks while one is running.
Practice
Solution
Step 1: Understand
mergeMapbehaviormergeMapsubscribes to all inner observables immediately and merges their outputs as they arrive.Step 2: Compare with other operators
concatMapwaits for each inner observable to complete before starting the next,exhaustMapignores new inner observables if one is active, andswitchMapcancels previous inner observables.Final Answer:
mergeMap -> Option DQuick Check:
Runs all tasks at once = mergeMap [OK]
- Confusing concatMap with mergeMap because both handle multiple tasks
- Thinking exhaustMap runs all tasks simultaneously
- Mixing switchMap behavior with mergeMap
concatMap in an Angular RxJS pipe?Solution
Step 1: Recall RxJS pipe usage
Operators likeconcatMapare used insidepipe()method on observables.Step 2: Check syntax correctness
source$.pipe(concatMap(value => inner$)) correctly usessource$.pipe(concatMap(...)). Options A and B misuse method chaining or function call. source$.pipe(map(concatMap(value => inner$))) wrongly nestsconcatMapinsidemap.Final Answer:
source$.pipe(concatMap(value => inner$)) -> Option BQuick Check:
Use pipe with concatMap inside [OK]
- Calling concatMap directly on observable without pipe
- Passing concatMap as argument to map
- Using concatMap as a standalone function
import { of } from 'rxjs';
import { concatMap, delay } from 'rxjs/operators';
of(1, 2, 3).pipe(
concatMap(x => of(x).pipe(delay(100 - x * 10)))
).subscribe(console.log);Solution
Step 1: Understand concatMap behavior with delays
concatMapwaits for each inner observable to complete before starting the next, preserving order.Step 2: Analyze delays and emission order
Even though delays differ (90ms, 80ms, 70ms), concatMap queues them and emits in source order: 1, then 2, then 3.Final Answer:
[1, 2, 3] -> Option CQuick Check:
concatMap preserves order despite delays [OK]
- Assuming output order matches delay times
- Confusing concatMap with mergeMap behavior
- Ignoring that concatMap queues inner observables
exhaustMap. It should ignore new clicks while processing, but it processes all clicks anyway. What is the likely mistake?
buttonClicks$.pipe( exhaustMap(() => fetchData()) ).subscribe();
Solution
Step 1: Check exhaustMap inner function return type
exhaustMapexpects the function to return an observable. IffetchData()returns a promise, it won't behave correctly.Step 2: Understand effect of returning a promise
Promises are not cancellable observables, so exhaustMap can't ignore new clicks properly, causing all clicks to process.Final Answer:
fetchData() returns a promise, not an observable -> Option AQuick Check:
exhaustMap needs observables, not promises [OK]
- Thinking exhaustMap works with promises automatically
- Forgetting to subscribe to the observable
- Confusing exhaustMap with mergeMap usage
searchInput$.pipe( ???(query => performSearch(query)) ).subscribe(results => display(results));
Solution
Step 1: Understand the requirement
The user wants to ignore new search inputs while a search is running, preventing duplicates.Step 2: Match operator behavior
exhaustMapignores new inner observables if one is active, perfect for this use case.mergeMapruns all,concatMapqueues,switchMapcancels previous.Final Answer:
exhaustMap because it ignores new searches while one is running -> Option AQuick Check:
Ignore new inputs during active task = exhaustMap [OK]
- Choosing switchMap which cancels previous instead of ignoring
- Using mergeMap which runs all searches simultaneously
- Using concatMap which queues instead of ignoring
Ready to prove it?
Pass the test and this pattern moves unknown
