Я использую наблюдаемую для запроса моей БД. Этот наблюдаемый вернет массив со всеми найденными совпадающими объектами. Моя проблема в том, что я хочу отобразить наблюдаемое с более подробной информацией, которую я получу из другого 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" - это нерабочий способ, который, как я ожидал, будет работать.
@martin the mergeMap
вызывает у меня ту же проблему, что и concatMap
. (И на самом деле это очень похожие операторы. Я ищу другое решение)
Если mergeMap
ведет себя так же, посмотрите, что assistsAPI$.find()
, потому что он, вероятно, не испускает предметы, как вы ожидаете.
Бэкенд — FeathersJS. Я уверен в том, что выдает AssistAPI$.find(). Дело не в этом. Но я благодарен за то, что вы пытаетесь помочь, спасибо :)
Вы можете использовать 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$
Я верю, что вы хотите 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()
, как предполагалось.
Вы должны каким-то образом обработать свой внутренний массив Observables. Вы можете использовать forkJoin
, merge
или concat
в зависимости от ваших потребностей.
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()
, как вы тоже предложили)!
Вы можете использовать
mergeMap
вместоconcatMap
.