flatMap, mergeMap, switchMap and concatMap in rxjs?
Asked Answered
E

8

130

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.

Efik answered 6/4, 2018 at 18:2 Comment(1)
flatMap is an alias for mergeMap.Extenuatory
H
209

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 for mergeMap and flatMap 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>
Heliolatry answered 6/4, 2018 at 22:47 Comment(3)
When you click 'flatMap', it emits "mergeMap completed" - bug or a typo?Vocalism
Why can't any of the docs be this succinct? Always so much faffing so I forget how the previous one works before I've finished reading the next one.Irina
@MarcinPevik flatMap is an alias for mergeMap which is why it prints out mergeMap. flatMap will be removed from RxJS in v8.Extrapolate
E
99

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:

mergeMap vs exhaustMap vs switchMap vs concatMap

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

Eckhart answered 3/5, 2019 at 9:25 Comment(1)
marbles are the best way imo with observables. a lot of these answers will make little sense otherwise to some peopleAmplexicaul
C
50

@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>
Cypripedium answered 8/4, 2018 at 21:46 Comment(1)
I would like to point out for the "mergeMap vs switchMap" section that mergeMap is also unpredictable, as different requests can take different completion times, which changes the ordering of the final emits. Let's say you want to display only the last emitted value, but it must be the last value for the last given input (outer emit), then neither suits you and you are forced to use concatMap instead. (This is theoretical, though, and I cannot think of a real-world example for this requirement. Anyways the main point is that both mergeMap and switchMap can be unpredictable.)Sofar
C
32

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.

Commercialism answered 14/9, 2021 at 5:9 Comment(4)
nice analogy. Thanks for the answerEfik
Though you have answered it almost 7 years back, I found it very useful to understand in one read. Thanks a ton!!Calendre
@AjeetSingh I answered this on septemper 14 2021, not in year 2014 :)Commercialism
:-D, Sorry my bad, Sep 14, I read as Sep 2014. But your answer saved my a lot of time.Calendre
M
17

Here is one more way of thinking about the difference between the different types of maps. This helped me get my head around it. I hope it might help others.

Consider the following sources:

  • A source producing lower case letters from the alphabet: a,b,c & d
  • 4 separate "word" sources, each one producing 3 words starting with a particular letter from the alphabet - a,b,c or d - then completing

To illustrate the difference between different kinds of maps, we will link items from the alphabet source to their "word" sources corresponding to that letter of the alphabet, using each different map to see the different outcomes.

Map

This is unlike the other maps because it does not introduce another source of observable. It just transforms an incoming value to another value.

So output from the lower case source, going through a Map which transforms input to upper case, would just be:

Input: a,b,c,d

Output: A, B, C, D

Switch Map

This transforms each input into another source, switching output to come from that new source (ie subscribing to that new source). When another alpha input arrives, the "word" source changes (we unsubscribe from the previous "word" source).

Input: a,b,c,d

Output: animal, aardvark, bull, baker, beach, cow, dog, day, dinner

Concat Map

Like switchMap except that Concat waits until each source completes before moving on to the next.

Input: a,b,c,d

Output: animal, aardvark, axe, bull, baker, beach, cow, car, cat, dog, day, dinner

Exhaust Map

Like Concat Map except that it will ignore any inputs that come in while it is still completing the last source. The example below assumes that the alpha inputs "b" and "d" both came in while the previous mapped source was still completing, so they were ignored.

Input: a,b,c,d

Output: animal, aardvark, axe, cow, car, cat

Merge Map (aka Flat Map)

Like concatMap in that each source runs to completion, but a new source can start up while other sources are still going - so the sequences overlap.

Input: a,b,c,d

Output: animal, aardvark, bull, axe, baker, cow, car, beach, dog, day, cat, dinner
Manana answered 6/9, 2020 at 4:19 Comment(0)
G
7

This is at the beginning a bit long to grasp, at least for me.

Anyways, consider this:

flatMap IS ANOTHER NAME FOR mergeMap - mergeMap method accepts an optional parameter concurrency, which defines how many Observables can be subscribed at the same time

concatMap equals mergeMap with concurrency set to 1

with mergeMap you do not lose any event emitted by the Observables you are merging as you suggested in your answer

switchMap works as you have described (see this nice article for more details https://blog.angular-university.io/rxjs-switchmap-operator/)

Glenine answered 6/4, 2018 at 20:43 Comment(0)
D
7
  1. switchMap - Stop working on the order and start working on the new order. Only the latest order will eve be finished.

  2. concatmap - The order gets added to a queue. You finish whatever order you're working on. Once you finish the order, you will work on the next order.

  3. mergeMap - You will work on all orders at the same time as soon as you're given them.

  4. exhaustMap - You ignore new orders and finish whatever order you're working on. Once finished, you are free to accept new orders.

Dewhurst answered 27/11, 2022 at 17:14 Comment(0)
G
6

I made a little demo/example for using the requested operators a while ago.

https://stackblitz.com/edit/rxjs-map-operators

You can choose between an interval or click for emiting outer observable values. For the inner observable you can choose whether to emit an interval (3 items) or a http request.

It will print the results below the selection.

Goglet answered 8/4, 2019 at 8:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.