Есть ли оператор, который работает как concatMap, но с более чем одним внутренним наблюдаемым

Я использую наблюдаемую для запроса моей БД. Этот наблюдаемый вернет массив со всеми найденными совпадающими объектами. Моя проблема в том, что я хочу отобразить наблюдаемое с более подробной информацией, которую я получу из другого API.

Я попробовал concatMap, но он позволил мне вложить только 1 наблюдаемый объект в исходный наблюдаемый объект.

const usersAPI$: Observable<Users[]> = //  something
const assistsAPI$: Observable<Assists[]> = //  something

const modifiedAssists$ = assistsAPI$.find().pipe(
  concatMap(assists =>
    assists.map(assist => usersAPI$.get(assist.userID)
       .pipe(
          map(user => 
            assist = {...assist}, ...{userName: user.userName}
          )
        )
    )
  )
);

Вы можете увидеть аналогичный рабочий пример здесь, на stackblitz, https://stackblitz.com/edit/rxjs-concatmap-issue-stackoverflow где "результат" - это правильный способ использования concatMap и "result2" - это нерабочий способ, который, как я ожидал, будет работать.

Вы можете использовать mergeMap вместо concatMap.

martin 08.04.2019 08:57

@martin the mergeMap вызывает у меня ту же проблему, что и concatMap. (И на самом деле это очень похожие операторы. Я ищу другое решение)

J Rui Pinto 08.04.2019 10:04

Если mergeMap ведет себя так же, посмотрите, что assistsAPI$.find(), потому что он, вероятно, не испускает предметы, как вы ожидаете.

martin 08.04.2019 10:13

Бэкенд — FeathersJS. Я уверен в том, что выдает AssistAPI$.find(). Дело не в этом. Но я благодарен за то, что вы пытаетесь помочь, спасибо :)

J Rui Pinto 08.04.2019 13:41
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
6
4
5 327
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете использовать combineLatest() (как статический импорт без канала)

import {combineLatest} from 'rxjs';

const usersAPI$: Observable<Users[]> = //  something
const assistsAPI$: Observable<Assists[]> = //  something

const combinedObservable$ = combineLatest(usersAPI$,assistsAPI$, someOtherStuff$);

const modifiedAssists$ = combinedObservable$.pipe(
   map([usersApiValues, assistsAPIValues, someOtherStuffValues] => { 
      /** what you want here */
   }) 
);

Просто примите во внимание, что каждый Observables внутри combineLatestдолжен излучать хотя бы один раз (например, вы можете запустить их с пустым массивом)

Мне нужно знать, сколько assistsAPIValues у меня есть, прежде чем запрашивать usersApiValues. Мне нужно преобразовать наблюдаемую assistsAPI$ так, чтобы она возвращала тот же массив из потока, но со свойством userName, уже назначенным каждому assist в assistsмассиве, возвращаемом assistsAPI$

J Rui Pinto 08.04.2019 10:15

Я верю, что вы хотите mergeMap?

Он отображает внешнюю наблюдаемую в разрешенную внутреннюю наблюдаемую для каждого внешнего излучения.

const result = clicks.pipe(
  concatMap(ev =>
    interval(1000).pipe(take(4))
  )
);

const result2 = clicks.pipe(
  mergeMap(ev =>
    arr.map(() => interval(1000).pipe(take(4)))
  )
);
result.subscribe(x => {
  console.info(x)
});

result2.subscribe(x => {
  console.info(x)
});

mergeMap (как concatMap ) не решает наблюдаемые, созданные внутренним arr.map(), как предполагалось.

J Rui Pinto 08.04.2019 10:08
Ответ принят как подходящий

Вы должны каким-то образом обработать свой внутренний массив Observables. Вы можете использовать forkJoin, merge или concat в зависимости от ваших потребностей.

forkJoin

forkJoin будет обрабатывать ваши внутренние вызовы API параллельно и возвращать массив после завершения всех вызовов API. Обратите внимание, что внутренние Observable должны завершиться. Я думаю, это то, что должно подойти вам в вашем приложении.

import { forkJoin } from 'rxjs';

const modifiedAssists$ = assistsAPI$.find().pipe(
  concatMap(assists => forkJoin(assists.map(assist => someObservable ))
);

сливаться

merge подпишется сразу на все внутренние вызовы API и выдаст результаты всех вызовов один за другим по мере их поступления.

import { merge } from 'rxjs';

const modifiedAssists$ = assistsAPI$.find().pipe(
  concatMap(assists => merge(...assists.map(assist => someObservable )),
  // toArray() add the toArray operator if you need the result as an array
);

Это даст вам желаемый результат в вашем stackblitz, но я думаю, что ваш stackblitz несколько вводит в заблуждение, а не то, что вы ищете в примере кода из вашего вопроса, поскольку внутренний Observable в вашем stackblitz испускает несколько раз, а окончательный вывод не массив. Если порядок внутренних запросов не имеет значения, все они выдают одно значение, а затем завершаются, и вам нужны результаты всех запросов в виде массива, просто используйте forkJoin.

конкат

concat подпишется на все вызовы внутреннего API один за другим. Следующий Observable в последовательности будет подписан только после завершения предыдущего. Поэтому выполнение будет медленнее, чем с forkJoin или merge, поскольку следующий HTTP-запрос будет выполнен только после того, как предыдущий вернет значение. Используйте это, если вызовы вашего userAPI должны быть сделаны в том же порядке, что и assists в вашем массиве.

import { concat } from 'rxjs';

const modifiedAssists$ = assistsAPI$.find().pipe(
  concatMap(assists => concat(...assists.map(assist => someObservable )),
  // toArray() add the toArray operator if you need the result as an array
);

Спасибо! Ваше решение concat сработало очень хорошо для той цели, которую я хотел (плюс с toArray(), как вы тоже предложили)!

J Rui Pinto 08.04.2019 15:06

Другие вопросы по теме