I have some trouble to understand this. When I use the switchMap operator with an Observable it emits all the values as expected:
Observable.from([1, 2, 3, 4, 5])
.do(console.log)
.switchMap(i => Observable.of('*' + i))
.do(console.log)
.subscribe();
Results:
1
*1
2
*2
3
*3
4
*4
5
*5
But when I replace the the Observable by a Promise I get a different behaviour:
Observable.from([1, 2, 3, 4, 5])
.do(console.log)
.switchMap(i => new Promise((resolve) => resolve('*' + i)))
.do(console.log)
.subscribe();
Results:
1
2
3
4
5
*5
The switchMap operator maps each value to an observable, then it flattens all of the inner observables. It basically projects each source value to an observable which is then merged in the output observable, emitting values only from the most recently projected observable.
So here's the simple difference — switchMap cancels previous HTTP requests that are still in progress, while mergeMap lets all of them finish. In my case, I needed all requests to go through, as this is a metrics service that's supposed to log all actions that the user performs on the web page, so I used mergeMap .
RxJS switchMap() operator is a transformation operator that applies a project function on each source value of an Observable, which is later merged in the output Observable, and the value given is the most recent projected Observable.
Use mergeMap if you simply want to flatten the data into one Observable, use switchMap if you need to flatten the data into one Observable but only need the latest value and use concatMap if you need to flatten the data into one Observable and the order is important to you.
This works as expected. The unexpected behaviors as you said is because Observable.from
and Observable.of
are always strictly synchronous while new Promise
necessarily doesn't (I'm not sure about the specification so maybe that's what Promise has to do in all browsers).
Anyway you can force Observable.from
emit asynchronously by passing the async
Scheduler.
import { Observable } from 'rxjs';
import { async } from 'rxjs/scheduler/async';
Observable.from([1, 2, 3, 4, 5], async)
.do(console.log)
.switchMap(i => new Promise((resolve) => resolve('*' + i)))
.do(console.log)
.subscribe();
Now every emission is in a new frame just like Promise
and the output is as you expected.
See live demo (open console): https://stackblitz.com/edit/rxjs5-gfeqsn?file=index.ts
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With