【问题标题】:RxJs: multiple requests followed by one request via concatMap not workingRxJs:多个请求后跟一个通过 concatMap 的请求不起作用
【发布时间】:2021-12-06 11:21:25
【问题描述】:

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

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

我也有一些稍微修改过的代码在它工作的地方,但是最终的请求是针对数组中的每个项目执行的,它只需要执行一次。

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

【问题讨论】:

    标签: angular rxjs concatmap


    【解决方案1】:
    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
        );
      })
    );
    

    【讨论】:

    • 谢谢!我认为您的答案更适合我的解决方案。我在下面发布了我的最终解决方案,其中包含更多连接
    【解决方案2】:

    您可以使用 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... */)
    );
    

    连接方法

    // 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$ 的末尾可能会很有用,因为这会导致返回结果是条形码保存结果数组的元组,并且保存货件的结果。

    【讨论】:

    • 太棒了,就像一个魅力!我认为添加变量也使代码更加清晰。它确实说 concat 已被弃用,所以我使用了: return of(saveBarCodes$, saveShipment$, transsmartLabels$).pipe( concatAll() );
    • concat 有两个版本 - 运算符版本和生成器版本。如果您收到已弃用的消息,那么您导入了错误的消息。运算符版本已替换为 concatWith。
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多