简体   繁体   中英

Combine observable responses into one

How can I obtain an object response from multiple observables?

My Epic is likes this:

export function metaDataEpic(action$: ActionsObservable<metaDataActions>) { 
    return action$.ofType(META_DATA_REQUEST)
      .mergeMap((action: metaDataActions) => {
        const { language } = action;
        return Observable.merge(
          Observable.fromPromise(couriersApi().then(r => r.json())),
          Observable.fromPromise(itemCategoriesApi({ language }).then(r => r.json())),
          Observable.fromPromise(airportsApi().then(r => r.json())))
          .flatMap(res => {
            console.log(res);
            return Observable.of(getMetaDataSuccess({}))
          });
      });
}

What I need is to pass my function getMetaDataSuccess() an object with the responses from courierApi() , itemCategoriesApi() and airportsApi() like this getMetaDataSuccess({ couriers, items, airports }) but first i need to wait for all 3 promises to finish and somehow combine in an object as response.

What my code does now is print the responses from the API's and thus calling getMetaDataSuccess 3 times. enter code here

Sounds like you want something like this.

export function metaDataEpic(action$: ActionsObservable<metaDataActions>) { 
    return action$.ofType(META_DATA_REQUEST)
      .flatMap(({language}) => Promise.all([
            couriersApi(),
            itemCategoriesApi({language}),
            airportsApi()
          ].map(p => p.then(r => r.json()))
        )
        .then(([couriers, items, airports]) => ({couriers, items, airports}))
      )
      .do(console.log)
      .map(getMetaDataSuccess);
}

flatMap aka mergeMap can operate directly on Promise and Arraylike values. Here we are using Promise.all to wait for the results of multiple Promises. Promise.all returns a Promise that resolves to an array where each element is the result of the corresponding item in the input array. We transform this array into an object and pass it along.

Here it is in action with dummy data demonstrating that it works.

 function couriersApi() { return Promise.resolve({ json: () => [{ name: 'x', id: 1 }, { name: 'y', id: 2 }] }); } function airportsApi() { return Promise.resolve({ json: () => [{ name: 'ORF', id: 1 }, { name: 'JFK', id: 2 }] }); } function itemCategoriesApi({language}) { return Promise.resolve({ json: () => [{ name: 'a', language: 'German' }, { name: 'b', language: 'English' }, ].filter(c => c.language === language) }); } const META_DATA_REQUEST = undefined; const action$ = { ofType: () => Rx.Observable.of({ language: 'English' }) }; metaDataEpic(action$) .subscribe(); function getMetaDataSuccess(o) { console.log(o.couriers); console.log(o.items); console.log(o.airports); } function metaDataEpic(action$) { return action$.ofType(META_DATA_REQUEST) .flatMap(({ language }) => Promise.all([ couriersApi(), itemCategoriesApi({ language }), airportsApi() ].map(p => p.then(r => r.json()))) .then(([couriers, items, airports]) => ({ couriers, items, airports })) ) .do(console.log) .map(getMetaDataSuccess); } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script> 

I have two solutions on my mind, you can try if they work in your case:

export function metaDataEpic(action$: ActionsObservable<metaDataActions>) {
    return action$
    .ofType(META_DATA_REQUEST)
    .mergeMap((action: metaDataActions) => {
        const {language} = action;

        const requestObservable = Observable.zip(
            Observable.fromPromise(couriersApi().then(r => r.json())),
            Observable.fromPromise(
                itemCategoriesApi({language}).then(r => r.json())
            ),
            Observable.fromPromise(airportsApi().then(r => r.json())),
            (couriers, items, airports) => {
                return Observable.of(getMetaDataSuccess({ couriers, items, airports })) ).map(response => response.value)
            }
        ).catch(e => {
            return Observable.of(getMetaDataFail(e));
        });

        return requestObservable;
    });
}

or

export function metaDataEpic(action$: ActionsObservable<metaDataActions>) {
    return action$
    .ofType(META_DATA_REQUEST)
    .mergeMap((action: metaDataActions) => {
        const {language} = action;

        Promise.all(
            [couriersApi(), itemCategoriesApi({language}), airportsApi()].map(p =>
                p.then(r => r.json())
            )
        )
        .then(([couriers, items, airports]) => {
            return Observable.of(getMetaDataSuccess({couriers, items, airports}));
        })
        .catch(e => {
            return Observable.of(getMetaDataFail(e));
        });
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM