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

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

我进行了一个 api 调用,其中 returns 一个数组。我需要遍历这个数组并为每个项目执行一个新请求。以下代码工作正常:

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

但是,当所有项目都完成后,我想提出另一个要求。就一个。

但是当我使用下面的代码时,当 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
        );
      })
    );
  })

我也有一些稍微修改过的代码,但后来对数组中的每个项目执行了最终请求,它只需要执行一次。

有人知道如何解决这个问题吗?提前致谢!!

  1. concatMap 之类的高阶运算符通过管道传输到来自 from 函数的流会为其源数组的每个元素触发它。您需要使用像 last 这样的运算符来将流限制为流中特定点的最后一个元素。

  2. 虽然管道 last 到内部或外部 observable 没有什么不同,但我更喜欢将它管道到外部 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
    );
  })
);

您可以使用 toArray 将一堆单独的排放组合成一个数组,或者您可以使用 concat 来处理连续的两个观察值,因为您似乎真的不想要保存条形码的结果。

我更喜欢第二种,但两者都符合您的要求,而且看起来比现有的更干净。

ToArray 方法

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

Concat 方法

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

如果将 toArray 添加到 saveBarCode$ 的末尾可能会很有用,因为这会导致 return 结果是条形码保存结果数组的元组和保存货件的结果。

这是我根据 Michael D 的回答得出的最终解决方案。最终的 concatMap 应该导致在 printViaIpAddress 方法中的每个项目的 subscribe 方法中命中成功方法。这非常有效。

Daniel 的回答最终导致每个串联的可观察对象都成功调用了方法(我认为),这在这种情况下并不是我想要的。

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