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');
},
});
In this example,
switchMapandmergeMaphave 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 tryconcatMaptoo?.The difference:
You'll notice how mergeMap keeps every incoming 'click' from the document. If you 'click' twice, there will be two calls to
getDateand 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
getDatecomplete, you'll see your stream is done.Try this with
switchMapand you'll notice that if you click more than once in a 3 second interval, the oldest click is dropped in favor of the newest one.switchMapwill 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
mergeMapdoesn't mean "merge the items these streams emit." It means "merge these streams".An update:
For each value emitted by the source observable, mergeMap returns a new inner observable exactly once.
These two observables have the same behavior:
If
of("value")emits one value and completes. If merged with another stream,of("value")still just emits a single value and completes.intervalemits many values and never completes, so you should expect any observable that an interval is merged into to emit many values and never complete.emits:
Neither
mergenormergeMapwillunsubscribeand thenresubscribeto an inner observable. They both subscribe once and will let the inner observable naturally complete.If you're not seeing the date emitted on the merged observable, then there's likely a bug in your code. While you shouldn't see a date re-emitted, you should still see it once the same way you would with the inner observable on it's own (that's what it means to merge).
Here's a new operator that resubscribes to every inner observable every time a new value is emitted by the source. Not sure if that's helpful, but in case you'd like to play around with the differences: