Bird
Raised Fist0
Angularframework~20 mins

mergeMap vs concatMap vs exhaustMap in Angular - Practice Questions

Choose your learning style10 modes available
Challenge - 5 Problems
🎖️
RxJS Mapping Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
❓ component_behavior
intermediate
2:00remaining
Behavior of mergeMap with overlapping inner observables
Given the following Angular component code using mergeMap, what will be the order of console logs when the button is clicked twice quickly?
Angular
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject, of } from 'rxjs';
import { delay, mergeMap, takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-test',
  template: `<button (click)="clickHandler()">Click me</button>`
})
export class TestComponent implements OnInit, OnDestroy {
  private clicks = new Subject<void>();
  private destroy$ = new Subject<void>();

  ngOnInit() {
    this.clicks.pipe(
      mergeMap(() => of('inner').pipe(delay(1000))),
      takeUntil(this.destroy$)
    ).subscribe(value => console.log(value));
  }

  clickHandler() {
    console.log('clicked');
    this.clicks.next();
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}
Aclicked, clicked, inner, inner
Bclicked, inner, clicked, inner
Cinner, inner, clicked, clicked
Dinner, clicked, inner, clicked
Attempts:
2 left
💡 Hint
Remember that mergeMap subscribes to all inner observables immediately and runs them concurrently.
❓ component_behavior
intermediate
2:00remaining
concatMap behavior with multiple rapid clicks
What will be the console output order when clicking the button three times quickly in this Angular component using concatMap?
Angular
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject, of } from 'rxjs';
import { delay, concatMap, takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-test',
  template: `<button (click)="clickHandler()">Click me</button>`
})
export class TestComponent implements OnInit, OnDestroy {
  private clicks = new Subject<void>();
  private destroy$ = new Subject<void>();

  ngOnInit() {
    this.clicks.pipe(
      concatMap(() => of('inner').pipe(delay(500))),
      takeUntil(this.destroy$)
    ).subscribe(value => console.log(value));
  }

  clickHandler() {
    console.log('clicked');
    this.clicks.next();
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}
Ainner, clicked, inner, clicked, inner, clicked
Bclicked, clicked, clicked, inner, inner, inner
Cclicked, inner, clicked, inner, clicked, inner
Dclicked, inner, inner, inner, clicked, clicked
Attempts:
2 left
💡 Hint
concatMap queues inner observables and runs them one after another.
❓ component_behavior
advanced
2:00remaining
exhaustMap behavior with rapid clicks
In this Angular component using exhaustMap, what will be the console output if the button is clicked three times quickly?
Angular
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject, of } from 'rxjs';
import { delay, exhaustMap, takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-test',
  template: `<button (click)="clickHandler()">Click me</button>`
})
export class TestComponent implements OnInit, OnDestroy {
  private clicks = new Subject<void>();
  private destroy$ = new Subject<void>();

  ngOnInit() {
    this.clicks.pipe(
      exhaustMap(() => of('inner').pipe(delay(700))),
      takeUntil(this.destroy$)
    ).subscribe(value => console.log(value));
  }

  clickHandler() {
    console.log('clicked');
    this.clicks.next();
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}
Aclicked, inner, clicked, inner, clicked, inner
Binner, clicked, clicked, clicked
Cclicked, inner, inner, clicked, clicked
Dclicked, clicked, clicked, inner
Attempts:
2 left
💡 Hint
exhaustMap ignores new inner observables while one is active.
📝 Syntax
advanced
2:00remaining
Identify the incorrect usage of mergeMap, concatMap, or exhaustMap
Which option contains a syntax or usage error in applying mergeMap, concatMap, or exhaustMap in Angular?
Asource$.pipe(exhaustMap(value => innerObservable(value)))
Bsource$.pipe(concatMap(value => innerObservable(value)))
Csource$.pipe(mergeMap(value => { innerObservable(value); }))
Dsource$.pipe(mergeMap(value => innerObservable(value)))
Attempts:
2 left
💡 Hint
Check if the arrow function returns the inner observable correctly.
🔧 Debug
expert
2:00remaining
Debugging unexpected behavior with concatMap and mergeMap
An Angular developer notices that when using concatMap in their code, inner observables run one after another, but when switching to mergeMap, all inner observables run at once. Which explanation best describes why this happens?
A<code>concatMap</code> queues inner observables and waits for each to complete before starting the next, while <code>mergeMap</code> subscribes to all inner observables immediately and runs them concurrently.
B<code>mergeMap</code> queues inner observables and waits for each to complete, while <code>concatMap</code> runs all inner observables concurrently.
C<code>concatMap</code> cancels previous inner observables when a new one starts, but <code>mergeMap</code> ignores new inner observables while one is active.
D<code>mergeMap</code> only runs the first inner observable and ignores the rest, while <code>concatMap</code> runs all inner observables concurrently.
Attempts:
2 left
💡 Hint
Think about how each operator handles multiple inner observables.

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