All topics
RxJSadvanced

Flattening Operators Compared: switchMap, mergeMap, concatMap, exhaustMap

Explain the distinct cancellation/concurrency semantics of the four main flattening operators and when to reach for each.

This is one of the single most-asked RxJS interview questions, because choosing the wrong flattening operator produces a subtle bug rather than a compile error — the app still works most of the time, until a race condition surfaces under real usage patterns like fast typing or rapid double-clicks. All four operators solve the same underlying problem (mapping each emitted value to a new inner Observable, typically an HTTP call, and flattening the result into a single output stream) but differ entirely in how they handle overlapping inner Observables.

Think of a single elevator (the flattening logic) handling call requests: switchMap abandons its current floor the instant a newer call comes in; mergeMap is like having infinite elevators, each answering its own call independently; concatMap is one elevator serving calls strictly in the order they arrived, finishing each trip before starting the next; exhaustMap is an elevator that ignores the call button entirely while already in transit.

Key Concepts

1
switchMap cancels the previous inner Observable the instant a new source value arrives, keeping only the latest — this is exactly right for a type-ahead search box, where only the most recent keystroke's results should ever reach the UI, and stale in-flight requests should be discarded rather than racing with newer ones.
switchMap
2
mergeMap (sometimes called flatMap) runs all inner Observables concurrently, with no cancellation and no ordering guarantee on completion — appropriate when every triggered request must complete regardless of order or timing, like firing off several independent analytics events that shouldn't interfere with each other.
mergeMapflatMap
3
concatMap queues inner Observables and runs them strictly one at a time, in order, only starting the next once the current one completes — the correct choice when order matters and requests must not overlap, like a sequence of dependent save operations that must apply in the exact order the user triggered them. exhaustMap ignores new source values entirely while an inner Observable is still active, only accepting a new one once the current finishes — the classic choice for a login/submit button, where you want to ignore rapid repeat clicks while the first request is still in flight rather than cancelling it or queuing duplicates.
concatMapexhaustMap
4
A strong interview answer frames the choice as a direct question about desired behavior on overlap: cancel-and-replace (switchMap), run-everything-concurrently (mergeMap), queue-and-run-in-order (concatMap), or ignore-until-done (exhaustMap) — memorizing this framing is more useful than memorizing the operator names in isolation.