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.