Home > database >  RXJS: Why is mergemap not providing output from multiple inner observables in example?
RXJS: Why is mergemap not providing output from multiple inner observables in example?

Time:07-05

I am trying to better understand mergemap by recreating a very simple example and it is not behaving as I expected from my understanding of the official description.

In this example each successive document click outputs a single result (i.e. the output of the "of" observable, which is behaviour I would have expected from switchmap. My understanding of mergemap is that it will merge the results of multiple inner observables which does not appear to be the case here.

To examine the lifecycle of the observable I included the verbose version of the subscribe() function so I could see if complete() is called, but it isn't, so I expected the results of multiple of observerables to accumulate with successive document click and be presented in the output on each click. However what happens is a single new observable result is created each time. Why does each successive document click only emit one observable stream, and discard the previous one. Isnt this the behaviour of switchmap not mergemap?

fromEvent(document, 'click')
  .pipe(mergeMap(() => of(Date.now())))
  .subscribe({
    next(x) {
      console.log('value ', x);
    },
    complete() {
      console.log('done');
    },
  });

CodePudding user response:

In this example each successive document click outputs a single result (i.e. the output of the "of" observable, which is behavior I would have expected from switchmap.)

In this example, switchMap and mergeMap have the same behavior.

Here's some code you can play around with to see where the two differ. Try this with both mergeMap, switchMap, and hey, why not try concatMap too?.

// Lets pretend we're grabbing the date from a server. 
// The server is slow, so this takes 3 seconds
function getDate(): Observable<number> {
  return timer(3000).pipe(
    map(_ => Date.now())
  );
}

fromEvent(document, 'click').pipe(
  take(5),
  mergeMap(getDate)
).subscribe({
  next: x => console.log('value ', x),
  complete: () => console.log('done')
});

The difference:

You'll notice how mergeMap keeps every incoming 'click' from the document. If you 'click' twice, there will be two calls to getDate and both calls will be kept active at the same time. 3 seconds later the first call will complete and emit a number and then some moments later the second call will complete and emit a number.

Once you've clicked 5 times and all the resulting calls to getDate complete, you'll see your stream is done.

Try this with switchMap and you'll notice that if you click more than once is a 3 second interval, the oldest click is dropped in favor of the newest one. switchMap will only allow a single inner observable (call to getDate) to be active at a time, so it cancels old ones and switches over to the new ones.


This all becomes more interesting when dealing with observables that emit more than once.


A quick note:

the merge in mergeMap doesn't mean "merge the items these streams emit." It means "merge these streams".

  • Related