简体   繁体   English

RxJs:多个请求后跟一个通过 concatMap 的请求不起作用

[英]RxJs: multiple requests followed by one request via concatMap not working

I make an api call which returns an array.我做了一个 api 调用,它返回一个数组。 I need to loop over this array and perform a new request per item.我需要遍历这个数组并为每个项目执行一个新请求。 The following code works fine:以下代码工作正常:

return this.expeditionService.getTranssmartBookingXml(request).pipe(
  concatMap((response) => {
    return from(response.barcodes).pipe(
      concatMap((barcode) => {
        return this.expeditionService.saveTranssmartBarcode({
          ...saveBarcodeRequest,
          barcode: barcode,
        });
      })
    );
  })
);

However, when all items are finished, I want to make another request.但是,当所有项目都完成后,我想提出另一个请求。 Just one.只有一个。

But when I use the following code, the process stops when the first item in the from array is finished.但是当我使用以下代码时,当 from 数组中的第一项完成时,该过程停止。

return this.expeditionService.getTranssmartBookingXml(request).pipe(
  concatMap((response) => {
    return from(response.barcodes).pipe(
      concatMap((barcode) => {
        return this.expeditionService.saveTranssmartBarcode({
          ...saveBarcodeRequest,
          barcode: barcode,
        });
      }),
      concatMap(() => {
        const transsmartSaveShipmentRequest: TranssmartSaveShipmentRequest =
          {
            reference: this.shipmentReferenceResult!.reference,
            price: this.transsmartDoBookingResponse!.price,
            trackingUrl: this.transsmartDoBookingResponse!.trackingUrl,
          };

        return this.expeditionService.saveTranssmartShipment(
          transsmartSaveShipmentRequest
        );
      })
    );
  })

I also had some slightly modified code where it was working, but then the final request was executed for each item in the array, where it only needs to execute once.我也有一些稍微修改过的代码,但随后对数组中的每个项目执行了最终请求,它只需要执行一次。

Does anybody know how to fix this?有人知道如何解决这个问题吗? Thanks in advance!!提前致谢!!

  1. Piping a higher order operator like concatMap to a stream from from function would trigger it for each element of it's source array. from function 将高阶运算符(如concatMap )连接到 stream 将为源数组的每个元素触发它。 You'd need to use an opeartor like last to restrict the stream only to the last element at a specific point of the stream.您需要使用类似last的操作符将 stream 限制为 stream 特定点的最后一个元素。

  2. Though it wouldn't make a difference piping last to either inner or outer observable, I'd prefer piping it to outer observable since it looks more elegant.虽然它不会对内部或外部last的管道产生影响,但我更喜欢将它管道到外部 observable,因为它看起来更优雅。

return this.expeditionService.getTranssmartBookingXml(request).pipe(
  concatMap((response) => {
    return from(response.barcodes).pipe(
      concatMap((barcode) => {
        return this.expeditionService.saveTranssmartBarcode({
          ...saveBarcodeRequest,
          barcode: barcode,
        });
      })
    )
  }),
  last(),
  concatMap(() => {
    const transsmartSaveShipmentRequest: TranssmartSaveShipmentRequest = {
      reference: this.shipmentReferenceResult!.reference,
      price: this.transsmartDoBookingResponse!.price,
      trackingUrl: this.transsmartDoBookingResponse!.trackingUrl,
    };

    return this.expeditionService.saveTranssmartShipment(
      transsmartSaveShipmentRequest
    );
  })
);

You can either use toArray to combine a bunch of separate emissions into a single array, or you can use concat to just process the two observables in serial since you really don't seem to want the results of saving barcodes.您可以使用toArray将一堆单独的发射组合成一个数组,或者您可以使用concat只处理两个可观察的序列,因为您似乎真的不想要保存条形码的结果。

I prefer the second, but both will do want you want and look cleaner than what you have.我更喜欢第二种,但两者都希望你想要并且看起来比你拥有的更干净。

ToArray Method ToArray 方法

return this.expeditionService.getTranssmartBookingXml(request).pipe(
  concatMap(res => from(res.barcodes)),
  concatMap(barcode => this.expeditionService.saveTranssmartBarcode({
    ...saveBarcodeRequest,
    barcode: barcode,
  })),
  toArray(),
  concatMap(() => /* save TrannsmartShipmentRequest... */)
);

Concat Method连接方法

// you don't have to create variables as you can place the streams directly in concat.
const saveBarCodes$ = this.expeditionService.getTranssmartBookingXml(request).pipe(
  concatMap(res => from(res.barcodes)),
  concatMap(barcode => this.expeditionService.saveTranssmartBarcode({
    ...saveBarcodeRequest,
    barcode: barcode,
  }))
);
// no point in this being inside an operator.
const saveShipmentRequest: TranssmartSaveShipmentRequest = {
  reference: this.shipmentReferenceResult!.reference,
  price: this.transsmartDoBookingResponse!.price,
  trackingUrl: this.transsmartDoBookingResponse!.trackingUrl
};
const saveShipment$ = this.expeditionService.saveTranssmartShipment(saveShipmentRequest);
return concat(saveBarCodes$, saveShipment$);

It could be useful if you added toArray to the end of saveBarCode$ since that would cause the return result to be a tuple of an array of barcode save results and the result from saving the shipment.如果将toArray添加到saveBarCode$的末尾可能会很有用,因为这会导致返回结果是条形码保存结果数组和保存货件的结果的元组。

Here is my final solution based on Michael D's answer.这是我基于 Michael D 的回答的最终解决方案。 The final concatMap should result in hitting the success method in the subscribe method for every item in the printViaIpAddress method.最终的 concatMap 应该导致在 printViaIpAddress 方法中的每个项目的 subscribe 方法中点击成功方法。 This works perfectly.这完美地工作。

The answer of Daniel resulted finally in hitting the success method for every concatenated observable (I think), which was not exactly what I wanted in this case. Daniel 的回答最终导致每个连接的 observable(我认为)都达到了成功方法,这并不是我在这种情况下想要的。

return this.expeditionService.getTranssmartBookingXml(request).pipe(
  concatMap((response) => {
    saveShipmentRequest.price = response.price;
    saveShipmentRequest.trackingUrl = response.trackingUrl;

    return from(response.barcodes).pipe(
      concatMap((barcode) => {
        return this.expeditionService.saveTranssmartBarcode({
          ...saveBarcodeRequest,
          barcode: barcode,
        });
      })
    );
  }),
  last(),
  concatMap(() => {
    return this.expeditionService.saveTranssmartShipment(
      saveShipmentRequest
    );
  }),
  concatMap(() => {
    return this.expeditionService.getTranssmartLabels(
      transsmartBonnenXmlRequest
    );
  }),
  concatMap((response) => {
    return from(response.values).pipe(
      concatMap((label) => {
        return this.expeditionService.printViaIpAddress({
          ...printTranssmartRequest,
          label,
        });
      })
    );
  })
);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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