flatMap, mergeMap, switchMap and concatMap in rxjs?
Someone, please explain the difference between SwitchMap and FlatMap in terms of Javascript ( in angular perspective, rxjs 5)
In my understanding.
SwitchMap only emits the latest observable value and cancels the previous observable.
flatMap collects all individual observables and returns all observables in a single array without caring about the order of observable. works asynchronously.
concatMap preserve the order and emits all observable value, works synchronously
is that right?
how does mergeMap works differently from above?
someone, please explain with an example.
Taking this from a previous answer:
-
flatMap/mergeMap - creates an Observable immediately for any source item, all previous Observables are kept alive. Note
flatMap
is an alias formergeMap
andflatMap
will be removed in RxJS 8. - concatMap - waits for the previous Observable to complete before creating the next one
- switchMap - for any source item, completes the previous Observable and immediately creates the next one
- exhaustMap - source items are ignored while the previous Observable is not completed
Here is an example of how each of the operators behaves when the source is immediate items (0,1,2,3,4) and the map function creates an Observable that delays each item by 500ms:
const { mergeMap, flatMap, concatMap, switchMap, exhaustMap } = Rx.operators;
const example = operator => () =>
Rx.Observable.from([0,1,2,3,4])
.pipe(
operator(x => Rx.Observable.of(x).delay(500))
)
.subscribe(console.log, () => {}, () => console.log(`${operator.name} completed`));
const mm = example(mergeMap);
const fm = example(flatMap);
const cm = example(concatMap);
const sm = example(switchMap);
const em = example(exhaustMap);
.examples > div {
cursor: pointer;
background-color: #4CAF50;
color: white;
padding: 7px 16px;
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.8/Rx.js"></script>
<div class='examples'>
<div onClick='mm()'>mergeMap </div>
<div onClick='fm()'>flatMap</div>
<div onClick='cm()'>concatMap</div>
<div onClick='sm()'>switchMap</div>
<div onClick='em()'>exhaustMap</div>
</div>
In the marble diagram below a source stream that emits at 5ms, 10ms, 20ms will be *Mapped to a timer(0, 3)
, limited to 3 emissions:
Play with this marble diagram here: "mergeMap vs exhaustMap vs switchMap vs concatMap"
Already having all these awesome answers, I wanted to add a more visual explanation
Hope it helps someone
@ZahiC, cool answer - I like the use of functional composition in the code sample. If I may, like to borrow it to illustrate a couple of extra points using timed observables.
Outer, inner, and control
These operators are all transformation operators like map()
, the common feature is they have an outer and inner observable. The key difference is the way the outer observable controls the inner observable.
To contrast them, my code sample runs them in pairs, outputting values in the form [outerValue,innerValue]
. I have added intervals to the test, and changed the inner delay so that there's some overlap in timing (formula used is delay((5-x)*200)
).
mergeMap vs concatMap
These both output all values, the difference is the ordering.
mergeMap - Order by inner observable
[0,0],[1,0],[0,1],[2,0],[1,1],[3,0],[2,1],[4,0],[3,1],[4,1]concatMap - Order by outer observable
[0,0],[0,1],[1,0],[1,1],[2,0],[2,1],[3,0],[3,1],[4,0],[4,1]
From the output, mergeMap outer emit can be delayed in the sequence, but concatMap follows strict outer emit sequence.
switchMap vs exhaustMap
These both throttle the output.
switchMap - Throttle by last
[3,0],[4,0],[4,1]exhaustMap - Throttle by first
[0,0],[0,1],[4,0],[4,1]
From the output, switchMap throttles any incomplete inner emits, but exhaustMap throttles following emits until the earlier ones complete.
mergeMap vs switchMap
I threw this in because switchmap is often used in SO answers where really mergeMap should be used.
mergeMap - Order by inner observable
[0,0],[1,0],[0,1],[2,0],[1,1],[3,0],[2,1],[4,0],[3,1],[4,1]switchMap - Throttle by last
[3,0],[4,0],[4,1]
The main takeaway is that the switchMap output is unpredictable depending on the timing of the inner observable, e.g if the inner is an http get the results can depend on connection speed.
console.clear()
const { mergeMap, flatMap, concatMap, switchMap, exhaustMap, delay, map, take, toArray } = Rx.operators;
const note = {
mergeMap: 'Order by inner observable',
concatMap: 'Order by outer observable',
switchMap: 'Throttle by last',
exhaustMap: 'Throttle by first',
}
const title = (operator) => {
const opName = operator.name.replace('$1','')
return `${opName} - ${note[opName]}`
}
const display = (x) => {
return map(y => `[${x},${y}]`)
}
const inner = (x) => Rx.Observable.timer(0,500)
.pipe(
delay((5-x)*200),
display(x),
take(2)
)
const example = operator => () => {
Rx.Observable.interval(500).take(5)
.pipe(
operator(x => inner(x)),
toArray(),
map(vals => vals.join(','))
)
.subscribe(x => {
console.log(title(operator))
console.log(x)
});
};
const run = (fn1, fn2) => {
console.clear()
fn1()
fn2()
}
const mmVcm = () => run(example(mergeMap), example(concatMap));
const smVem = () => run(example(switchMap), example(exhaustMap));
const mmVsm = () => run(example(mergeMap), example(switchMap));
.examples > div {
cursor: pointer;
background-color: #4CAF50;
color: white;
padding: 7px 16px;
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.8/Rx.js"></script>
<div class='examples'>
<div onClick='mmVcm()'>mergeMap vs concatMap </div>
<div onClick='smVem()'>switchMap vs exhaustMap</div>
<div onClick='mmVsm()'>mergeMap vs switchMap </div>
</div>
Let's say you are subscribed to a weather channel. weather announcer reads the report that passed to him after run through several operations.
-
If the announcer is reading one report and while reading another report comes in. If he stops reading the first report and begins to read the new report as soon as it arrives then he is doing
switchMap
. Because switchMap projects each source value to an observable which is merged in the output observable, emitting values only from the most "recently" projected observable. -
if the radio announcer does not begin a new report until the first one is finished, we have
concatMap
. concatMap projects each source value to an observable which is merged in the output observable in a serialized fashion waiting for each one to complete before merging the next. -
if a new report comes in while the announcer is still reading and his response is somehow to read both at the same time then we have
mergeMap/flatMap
. ( flatMap is an alias for mergeMap). Because mergeMap projects each source value to an observable which is merged in the output observable. "mergeMap" is a more basic version of switchMap and concatMap.