RxJS: оператор сканирования внутри switchMap / mergeMap

#typescript #rxjs

#typescript #rxjs

Вопрос:

Мы используем оператор сканирования для нашей кнопки «Загрузить больше» в нашей таблице. С помощью оператора сканирования мы просто суммируем новые результаты с предыдущими. Однако мы столкнулись с некоторым неожиданным для нас поведением.

Для упрощения предположим, что у нас есть следующий код:

 const fakeRequest = of('response').pipe(delay(2000));
interval(1000).pipe(
    mergeMap(_ => fakeRequest),
    scan<string>((allResponses, currentResponse) => [...allResponses, currentResponse], []),
).subscribe(console.log);
  

выдает:

 ["response"]
["response", "response"]
["response", "response", "response"]
["response", "response", "response", "response"]
...
  

В то время как, если мы просто переместим оператор сканирования внутрь оператора mergeMap / switchMap:

 interval(1000).pipe(
    mergeMap(_ => fakeRequest.pipe(
        scan<string>((allResponses, currentResponse) => [...allResponses, currentResponse], []),
    )),
).subscribe(console.log);
  

Мы получаем следующие результаты:

 ["response"]
["response"]
["response"]
["response"]
...
  

Оператор сканирования не выполняется во втором примере. Я бы ожидал, что switchMap / mergeMap просто сглаживает внутреннюю наблюдаемую, в которую встроен оператор сканирования.

  • Является ли это желаемым поведением?
  • Если да, кто-нибудь может объяснить, почему это происходит?
  • Если да, есть ли другой способ / обходной путь для достижения такого же поведения внутри операторов mergeMap или switchMap?

Спасибо!

Комментарии:

1. Я думаю, это связано с тем фактом, что scan () работает с отправленными данными, а mergeMap выполняет работу по подписке на внутреннюю наблюдаемую, прежде чем сопоставлять значение с внешней наблюдаемой. Если вы поместите оператор scan() в инструкцию объявления fakeRequest , возможно, будет более ясно, что сканирование не получает никаких значений из-за().канал (delay()) никогда не подписан на выдачу ‘response’.

2. ваш первый пример работает нормально, почему вы хотите переключиться на второй

3. @FanCheung 1. потому что наш наблюдаемый поддельный запрос поступает из сервиса. Мы хотим включить функциональность сканирования в нашу службу передачи данных, потому что она принадлежит там. Однако по какой-то причине нам пришлось использовать switchMap в нашем компоненте страницы. Теперь нам нужно добавить функциональность сканирования в наш компонент, чего мы не хотим, и это очень неожиданное поведение. Мы думали, что switchMap почти буквально «переключится» на внутреннюю наблюдаемую, включая оператора сканирования. 2. Просто пытаюсь разобраться в причудах. Мы не ожидали, что операторы внутренней наблюдаемой будут просто «игнорироваться».

Ответ №1:

Причина разницы заключается в том, что в первом случае:

 interval(1000).pipe(
    mergeMap(_ => fakeRequest),
    scan<string>((allResponses, currentResponse) => [...allResponses, currentResponse], []),
)
  

Вы scan удаляете выбросы из ВСЕХ наблюдаемых объектов, созданных внутри mergeMap .

Однако во втором случае:

 interval(1000).pipe(
    mergeMap(_ => fakeRequest.pipe(
        scan<string>((allResponses, currentResponse) => [...allResponses, currentResponse], []),
    )),
)
  

Вы scan отслеживаете выбросы из наблюдаемого объекта, который генерируется только один раз!

По сути, mergeMap() это источник, который генерирует несколько раз. fakeResponse Наблюдаемый объект является источником, который генерируется только один раз.

Из вашего комментария я понял, что причина, по которой вы хотели переместить scan под ответом, заключалась в желании выполнить scan в вашем сервисе, поэтому отдельным потребителям это не нужно. Я знаю, что это старый вопрос, так что, возможно, вы уже разобрались с этим, но вы, безусловно, можете выполнить логику сканирования в своем сервисе (при условии, что ваш interval () также присутствует там), выбрав .pipe(scan(...)) .