Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does switchMap operator only emit the last value when used with a promise?

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
like image 962
Martin Avatar asked Feb 17 '18 16:02

Martin


People also ask

What is one of the key benefits of the switchMap observable operator?

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.

What is the difference between mergeMap and switchMap?

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 .

What is switchMap operator in RxJS?

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.

When would you use a switchMap?

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.


1 Answers

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

like image 79
martin Avatar answered Oct 21 '22 19:10

martin