【问题标题】:How do I combine multiple http requests and merge results如何合并多个http请求并合并结果
【发布时间】:2019-10-21 12:34:09
【问题描述】:

我需要处理我有 3 个端点要调用并希望以最方便/有效的方式获取数据的情况。第一次调用可以独立处理并返回单个结果。第二个端点返回一个集合,但需要启动 0-* 后续调用,其中存在给定键。

理想情况下,希望将集合(来自第二个端点调用)作为包含来自第三个端点调用的结果的变异/新集合接收。

我目前正在使用 forkJoin(observableA$, observableB$) 来并行处理前 2 个调用,但我无法弄清楚如何包含顺序调用并将数据包含在 observableB$ 中

//Customer observable
const customer$ = this._customerManagementService.getCustomer(
  accountNumber
);
 return forkJoin({
      customer: customer$,
      saleCycles: saleCyclesWithVehicle$
    }).pipe(finalize(() => this._loaderFactoryService.hide()));
 getSalesWithVehicle(accountNumber: string, dealerKey: string) {
    return this._salesCycleService
      .getCyclesForCustomer({
        customerNumber: accountNumber,
        dealerKey: dealerKey
      })
      .pipe(
        concatMap((results: ISaleCycle[]) => {
          return results.map(cycle => {
            return this._purchaseVehicleService.getPurchaseVehicle(
              cycle.vehicleKey
            );
          });
        })
      );
  }

我希望该集合包含更多数据作为原始集合的新属性

更新

经过深思熟虑,也许我应该在解决方案的某个地方使用 reduce。这样我就可以控制什么被推入数组并且它可以是动态的?

  getSalesWithVehicle(accountNumber: string, dealerKey: string) {
    return this._salesCycleService
      .getCyclesForCustomer({
        customerNumber: accountNumber,
        dealerKey: dealerKey
      })
      .pipe(
        switchMap((results: ISaleCycle[]) => {
          return results.map(cycle => {
            if (cycle.vehicleKey) {
              return this._purchaseVehicleService
                .getPurchaseVehicle(cycle.vehicleKey)
                .pipe(
                  reduce((acc, vehicle) => {
                    return { cycle: cycle, vehicle: vehicle };
                  }, []),
                  toArray()
                );
            }
            else {
              ///No extra data to be had
            }
          });
        }),
        concatAll()
      );
  }

【问题讨论】:

    标签: http rxjs


    【解决方案1】:

    我会使用concatMap() 来合并 HTTP 请求 2 和 3 的响应。

    import { of } from 'rxjs'; 
    import { map, concatMap } from 'rxjs/operators';
    
    const pretendGetCustomer = of({accountNumber: 123, name:"John Doe"});
    
    const pretendGetVehiculeHttpRequest = (customerNumber) => {
      return of([{custNum: 123, vehicleId:"2"}, {custNum: 123, vehicleId:"1"}]);
    }
    const pretendGetCyclesHttpRequest = (cycleIds) => {
      return of([{id:"1", name:"yellow bike", retailPrice:"$10"}, {id:"2", name:"red bike", retailPrice:"$20"}]);
    }
    
    const yourFunction = () => {
      pretendGetCustomer.subscribe(customer => {
        // Assuming you do other things here with cust, reason why we are subscribing to this separately
        // isHappy(customer)
    
        // Your second & third calls
        pretendGetVehiculeHttpRequest(customer.accountNumber).pipe(
          // Need to use concatMap() to subscribe to new stream
          // Note: use mergeMap() if you don't need the 1st stream to be completed
          //       before calling the rest
          concatMap(purchases => {
            const cyclesIds = purchases.map(p => p.vehicleId);
            // concatMap() requires an Observable in return
            return pretendGetCyclesHttpRequest(cyclesIds).pipe(
              // Use map() here because we just need to use the data,
              // don't need to subscribe to another stream
              map(cycles=>{
                // Retrun whatever object you need in your subscription
                return {
                  customerNumber: customer.accountNumber,
                  customerName: customer.name,
                  purchases: purchases.map(p => cycles.find(c => p.vehicleId === c.id))
                }
              })
            );
          })
        ).subscribe(resultof2and3 => {
          // Do something with the new/mutated Object which is a result of
          // your HTTP calls #2 and #3
          console.log(resultof2and3);
        });
      });
    }
    
    yourFunction();
    

    如果你想看上面的运行,我做了一个 stackblitz(见控制台):https://stackblitz.com/edit/rxjs-nqi7f1

    【讨论】:

      【解决方案2】:

      这是我最终想出的解决方案。我接受了 BoDeX 的建议并使用了 concatMap()。在我看来,我很清楚我想使用 forkJoin 并能够通过对象键(即 customer 或 saleCycles)引用结果。

      在存在车辆密钥的场景中,我需要使用 map() 在定义的数据结构中返回结果。同样,如果没有找到车辆,那么我只需要外部 observable。

      const customer$ = this._customerManagementService.getCustomer(accountNumber);
      
      const saleCyclesWithVehicle$ = this.getSalesWithVehicle(accountNumber,dealerKey);
      
      getSalesWithVehicle(accountNumber: string, dealerKey: string) {
          return this._salesCycleService
            .getCyclesForCustomer({
              customerNumber: accountNumber,
              dealerKey: dealerKey
            })
            .pipe(
              concatMap(cycles => {
                return from(cycles).pipe(
                  concatMap((cycle: ISaleCycle) => {
                    if (cycle.vehicleKey) {
                      return this._purchaseVehicleService
                        .getPurchaseVehicle(cycle.vehicleKey)
                        .pipe(
                          map(vehicle => {
                            return { cycle: cycle, vehicle: vehicle };
                          })
                        );
                    } else {
                      return of({ cycle: cycle });
                    }
                  }),
                  toArray()
                );
              })
            );
        }
      
      return forkJoin({
            customer: customer$,
            saleCycles: saleCyclesWithVehicle$
          }).pipe(finalize(() => this._loaderFactoryService.hide()));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-31
        • 2020-08-20
        • 2010-09-11
        • 2019-11-03
        • 1970-01-01
        相关资源
        最近更新 更多