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.

  1. Konverter løftet til en observabel ved å bruke from.
  2. I den ytre røret, kall switchMap slik at du kan kalle en annen observable og returnere den som resultatet av den ytre observable. Du bytter kjøringskontekster.
  3. Inne i switchMap gjør du mappingen av subItems som du gjorde før, og bruk deretter forkJoin for å 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 som promise.all.
  4. 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.

Relaterte artikler