typescript - Angular/ RxJS: nestede tjenestekall med observables
Jeg er ny på RxJS-observabler, og jeg prøver å løse en ganske enkel brukstilfelle.
I en tjeneste gjør jeg først et HTTP-kall som returnerer et element (som en observabel). Elementet inneholder en array av ID-er, noen av dem gjentatt. For hver unik ID må jeg ringe en annen HTTP-tjeneste (som igjen returnerer en observabel), og legge verdien den returnerer til det opprinnelige elementet i stedet for den tilsvarende ID-en. Disse kallene bør skje parallelt. Til slutt, når alle kallene er fullført, skal tjenesten min returnere en observabel av det opprinnelige elementet, nå med sine under-elementer på plass.
For å gi en bedre idé, er dette hvordan det ville se ut med løfter i stedet for observabler:
MyService() {
return HttpGetMainItem()
.then(item => {
var promises = _.uniq(item.subItems)
.map(sid => HttpGetSubItem(sid)
.then(subItem => {
// add to matching item.subItems
}));
// wait for all promises to complete and return main item
return Promise.all(promises).then(() => item);
});
}
Hva ville være den beste måten å oppnå dette på når man arbeider med observabler?
REDIGER: fra svarene ser det ut til at jeg ikke var veldig klar. Eksemplet med promises er bare for tydelighet, i mitt tilfelle er HTTP-kallene faktisk Angulars HttpClient.get, så de returnerer observables - jeg ønsker å gjøre alt med observables.
Svar #1
Her er en måte du kan oppnå det ovennevnte på med rxjs.
- Konverter løftet til en observabel ved å bruke
from. - I den ytre røret, kall
switchMapslik at du kan kalle en annen observable og returnere den som resultatet av den ytre observable. Du bytter kjøringskontekster. - Inne i switchMap gjør du mappingen av subItems som du gjorde før, og bruk deretter
forkJoinfor å lage en Observable av alle elementenes promise. ForkJoin vil sende ut et array av alle resultatene når alle promise er fullført. Det er sompromise.all. - Legg til elementene som du hadde planlagt å gjøre før, og returner det opprinnelige elementet.
Kode
from(HttpGetMainItem()).pipe(
switchMap(item =>
forkJoin(_.uniq(item.subItems).map(sid => HttpGetSubItem(sid)))
.pipe(
map(results => {
/* add results to matching items.subItems and */
return item;
})
)
)
);
Jeg føler dette ser litt klumpete ut, på grunn av behovet for å beholde rotobjektet og den nestingen det krever. Du kan bruke velgerparameteren til switchMap for å kombinere den ytre og indre observabelen. Du kan bruke den parameteren i stedet for logikken du hadde i map(), og siden resultatene fra begge observablene sendes videre, vil du ikke trenge ytterligere nesting.
from(HttpGetMainItem()).pipe(
switchMap(
item => forkJoin(_.uniq(item.subItems).map(sid => HttpGetSubItem(sid))),
(item, results) => {
/* add results to matching items.subItems and */
return item;
}
)
);
Svar #2
Jeg tror du trenger noe slik:
import { from, forkJoin } from 'rxjs';
import { switchMap } from 'rxjs/operators';
itemWithSubItems$ = from(HttpGetMainItem()).pipe(
switchMap(item => {
const promises = _.uniq(item.subItems).map(item => HttpGetSubItem(item.sid));
return forkJoin(promises)
.map(resultArray => item.subItems = resultArray)
.map(_ => item);
})
);
Først henter du hovedelementet. Bruk deretter forkJoin for å løse alle underforespørsler og berike hovedelementet. Etter det returnerer du bare hovedelementet.
