Bird
Raised Fist0
Angularframework~30 mins

mergeMap vs concatMap vs exhaustMap in Angular - Hands-On Comparison

Choose your learning style10 modes available
Understanding mergeMap vs concatMap vs exhaustMap in Angular
📖 Scenario: You are building a simple Angular component that listens to button clicks and makes simulated API calls. You want to learn how different RxJS operators mergeMap, concatMap, and exhaustMap handle multiple clicks differently.
🎯 Goal: Create an Angular standalone component that uses mergeMap, concatMap, and exhaustMap separately to handle button click events and simulate API calls. Observe how each operator manages multiple rapid clicks.
📋 What You'll Learn
Create a button click event observable
Set up a simulated API call function returning an observable with delay
Use mergeMap to handle clicks and show results
Use concatMap to handle clicks and show results
Use exhaustMap to handle clicks and show results
Display the emitted values in the template for each operator
💡 Why This Matters
🌍 Real World
Handling user events like button clicks or search inputs where multiple requests can happen quickly, and choosing the right RxJS operator controls how these requests are managed.
💼 Career
Understanding these operators is essential for Angular developers to manage asynchronous data streams effectively and avoid bugs like race conditions or unnecessary API calls.
Progress0 / 4 steps
1
Set up the Angular component and simulated API call
Create a standalone Angular component named MapOperatorsComponent. Inside it, create a method called fakeApiCall that takes a value string and returns an observable that emits `Response: ${value}` after a 1 second delay using of and delay from RxJS.
Angular
Hint

Use of from RxJS to create an observable that emits a value. Use delay(1000) to simulate a 1 second wait.

2
Add a button click observable and a config variable
Add a public property clicks$ of type Subject<string> initialized as a new Subject. Also add a public property clickCount initialized to 0. This will track the number of clicks and emit values.
Angular
Hint

Import Subject from RxJS and create clicks$ as a new Subject of strings. Initialize clickCount to zero.

3
Use mergeMap to handle clicks and show results
Import mergeMap and tap from RxJS. Create a public property mergeMapResults$ that subscribes to clicks$ and uses mergeMap to call fakeApiCall with the emitted value. Use tap to log the response. Increment clickCount and emit `Click ${clickCount}` to clicks$ when a method onClickMergeMap() is called.
Angular
Hint

Use mergeMap inside pipe on clicks$. Create a method onClickMergeMap that increments clickCount and emits a string to clicks$.

4
Add concatMap and exhaustMap handling with template buttons and display
Import concatMap and exhaustMap from RxJS. Create two new public properties concatMapResults$ and exhaustMapResults$ that use concatMap and exhaustMap respectively on clicks$ to call fakeApiCall. Add two methods onClickConcatMap() and onClickExhaustMap() that increment clickCount and emit `Click ${clickCount}` to clicks$. Update the component template to have three buttons labeled MergeMap, ConcatMap, and ExhaustMap that call their respective click methods. Below each button, display the latest emitted value from the corresponding observable using the async pipe.
Angular
Hint

Use concatMap and exhaustMap similarly to mergeMap. Add buttons in the template that call the respective click methods. Display results with the async pipe.

Practice

(1/5)
1. Which RxJS operator runs all inner tasks at the same time and emits results as they complete, without waiting for order?
easy
A. switchMap
B. concatMap
C. exhaustMap
D. mergeMap

Solution

  1. Step 1: Understand mergeMap behavior

    mergeMap subscribes to all inner observables immediately and merges their outputs as they arrive.
  2. Step 2: Compare with other operators

    concatMap waits for each inner observable to complete before starting the next, exhaustMap ignores new inner observables if one is active, and switchMap cancels previous inner observables.
  3. Final Answer:

    mergeMap -> Option D
  4. Quick Check:

    Runs all tasks at once = mergeMap [OK]
Hint: All tasks at once means mergeMap [OK]
Common Mistakes:
  • Confusing concatMap with mergeMap because both handle multiple tasks
  • Thinking exhaustMap runs all tasks simultaneously
  • Mixing switchMap behavior with mergeMap
2. Which of the following is the correct syntax to use concatMap in an Angular RxJS pipe?
easy
A. concatMap(source$, value => inner$)
B. source$.pipe(concatMap(value => inner$))
C. source$.concatMap(value => inner$)
D. source$.pipe(map(concatMap(value => inner$)))

Solution

  1. Step 1: Recall RxJS pipe usage

    Operators like concatMap are used inside pipe() method on observables.
  2. Step 2: Check syntax correctness

    source$.pipe(concatMap(value => inner$)) correctly uses source$.pipe(concatMap(...)). Options A and B misuse method chaining or function call. source$.pipe(map(concatMap(value => inner$))) wrongly nests concatMap inside map.
  3. Final Answer:

    source$.pipe(concatMap(value => inner$)) -> Option B
  4. Quick Check:

    Use pipe with concatMap inside [OK]
Hint: Always use pipe() with concatMap inside [OK]
Common Mistakes:
  • Calling concatMap directly on observable without pipe
  • Passing concatMap as argument to map
  • Using concatMap as a standalone function
3. Given the code below, what will be the output order of emitted values?
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);
medium
A. [3, 2, 1]
B. [1, 3, 2]
C. [1, 2, 3]
D. [2, 1, 3]

Solution

  1. Step 1: Understand concatMap behavior with delays

    concatMap waits for each inner observable to complete before starting the next, preserving order.
  2. 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.
  3. Final Answer:

    [1, 2, 3] -> Option C
  4. Quick Check:

    concatMap preserves order despite delays [OK]
Hint: concatMap keeps order even if inner delays differ [OK]
Common Mistakes:
  • Assuming output order matches delay times
  • Confusing concatMap with mergeMap behavior
  • Ignoring that concatMap queues inner observables
4. You have this code snippet using exhaustMap. It should ignore new clicks while processing, but it processes all clicks anyway. What is the likely mistake?
buttonClicks$.pipe(
  exhaustMap(() => fetchData())
).subscribe();
medium
A. fetchData() returns a promise, not an observable
B. Using exhaustMap instead of mergeMap
C. Not subscribing to the observable
D. Not returning an observable inside exhaustMap

Solution

  1. Step 1: Check exhaustMap inner function return type

    exhaustMap expects the function to return an observable. If fetchData() returns a promise, it won't behave correctly.
  2. 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.
  3. Final Answer:

    fetchData() returns a promise, not an observable -> Option A
  4. Quick Check:

    exhaustMap needs observables, not promises [OK]
Hint: exhaustMap requires observables, not promises [OK]
Common Mistakes:
  • Thinking exhaustMap works with promises automatically
  • Forgetting to subscribe to the observable
  • Confusing exhaustMap with mergeMap usage
5. You want to handle user search input so that if a search is running, new inputs are ignored until it finishes. Which operator should you use and why?
searchInput$.pipe(
  ???(query => performSearch(query))
).subscribe(results => display(results));
hard
A. exhaustMap because it ignores new searches while one is running
B. concatMap because it queues searches and runs them one by one
C. mergeMap because it runs all searches simultaneously
D. switchMap because it cancels previous searches on new input

Solution

  1. Step 1: Understand the requirement

    The user wants to ignore new search inputs while a search is running, preventing duplicates.
  2. Step 2: Match operator behavior

    exhaustMap ignores new inner observables if one is active, perfect for this use case. mergeMap runs all, concatMap queues, switchMap cancels previous.
  3. Final Answer:

    exhaustMap because it ignores new searches while one is running -> Option A
  4. Quick Check:

    Ignore new inputs during active task = exhaustMap [OK]
Hint: Ignore new tasks while running = exhaustMap [OK]
Common Mistakes:
  • Choosing switchMap which cancels previous instead of ignoring
  • Using mergeMap which runs all searches simultaneously
  • Using concatMap which queues instead of ignoring
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard