【问题标题】:Combine observable responses into one将可观察到的响应合二为一
【发布时间】:2018-06-05 12:11:42
【问题描述】:

如何从多个 observable 中获取对象响应?

我的史诗是这样的:

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({}))
          });
      });
}

我需要传递我的函数 getMetaDataSuccess() 一个对象,其中包含来自 courierApi()itemCategoriesApi()airportsApi() 的响应,就像这样 getMetaDataSuccess({ couriers, items, airports }) 但首先我需要等待所有 3 个承诺完成并且以某种方式合并到一个对象中作为响应。

我的代码现在所做的是打印来自 API 的响应,从而调用 getMetaDataSuccess 3 次。 enter code here

【问题讨论】:

    标签: javascript react-native promise redux-observable


    【解决方案1】:

    我有两个解决方案,如果它们适用于您的情况,您可以尝试:

    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;
        });
    }
    

    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));
            });
        });
    }
    

    【讨论】:

    • 由于 rxjs,我最终使用了它。虽然我必须在 zip 中添加 .map(response =&gt; response.value 才能获得操作
    • 我知道你可以试试return getMetaDataSuccess({couriers, items, airports}); 而不是return Observable.of(getMetaDataSuccess({couriers, items, airports}));
    • 我必须同时使用两者才能使其工作像这样:return Observable.zip( Observable.fromPromise(couriersApi().then(r =&gt; r.json())), Observable.fromPromise(itemCategoriesApi({ language }).then(r =&gt; r.json())), (couriers, itemCategories) =&gt; Observable.of(getMetaDataSuccess({ couriers, itemCategories })) ).map(response =&gt; response.value)
    【解决方案2】:

    听起来你想要这样的东西。

    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 可以直接对 Promise 和 Arraylike 值进行操作。这里我们使用Promise.all 来等待多个 Promise 的结果。 Promise.all 返回一个 Promise,它解析为一个数组,其中每个元素都是输入数组中相应项的结果。我们将这个数组转换为一个对象并传递它。

    这里用虚拟数据证明它有效。

    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);
    }
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"&gt;&lt;/script&gt;

    【讨论】:

    • 您在r.json()) 处缺少一个括号,用于Promise.all( 另外,我尝试过,但从未点击过console.log
    • @user3610155 谢谢,我修正了错字并添加了现场演示。运行它,它可以工作。
    猜你喜欢
    • 1970-01-01
    • 2019-02-09
    • 1970-01-01
    • 2020-01-02
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    • 2020-01-17
    • 1970-01-01
    相关资源
    最近更新 更多