Почему оператор switchMap выдает только последнее значение при использовании с обещанием?

У меня есть некоторые проблемы, чтобы понять это. Когда я использую оператор switchMap с Observable, он выдает все значения, как и ожидалось:

Observable.from([1, 2, 3, 4, 5])
    .do(console.log)
    .switchMap(i => Observable.of('*' + i))
    .do(console.log)
    .subscribe();

Полученные результаты:

1
*1
2
*2
3
*3
4
*4
5
*5

Но когда я заменяю Observable обещанием, я получаю другое поведение:

Observable.from([1, 2, 3, 4, 5])
    .do(console.log)
    .switchMap(i => new Promise((resolve) => resolve('*' + i)))
    .do(console.log)
    .subscribe();

Полученные результаты:

1
2
3
4
5
*5

person Martin    schedule 17.02.2018    source источник


Ответы (1)


Это работает, как и ожидалось. Неожиданное поведение, как вы сказали, связано с тем, что Observable.from и Observable.of всегда строго синхронны, а new Promise обязательно нет (я не уверен в спецификации, поэтому, возможно, это то, что Promise должен делать во всех браузерах).

В любом случае вы можете заставить Observable.from испускать асинхронно, передав планировщик async.

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();

Теперь каждое излучение находится в новом кадре, как и Promise, и результат соответствует вашим ожиданиям.

Посмотреть живую демонстрацию (открытая консоль): https://stackblitz.com/edit/rxjs5-gfeqsn?file=index.ts

person martin    schedule 17.02.2018
comment
Это полностью имеет смысл! Но не могли бы вы пояснить, почему факт наличия выбросов в отдельных кадрах ведет себя именно так? Я ожидаю, что все выбросы будут выводиться. (Или я, вероятно, не понимаю ожидаемого поведения switchMap.) Спасибо. @Мартин - person Martin; 17.02.2018
comment
Это потому, что switchMap получает 1 и подписывается на new Promise(). Потом в этом же кадре получает 2 и отписывается от предыдущего Promise и подписывается на новый и так далее. И только после 5 и его Promise фрейм заканчивается, и JS оценивает другой фрейм, где разрешается только последний активный Promise. - person martin; 17.02.2018