动态地将CompletableStage与最终的错误传播结合起来

时间:2020-05-13 10:51:03

标签: java completable-future

我需要(根据计算结果)动态组合CompletionStages以便不阻止执行,最终我需要捕获操作期间可能出现的异常,以便仔细关闭执行。

我已经实现了以下类似内容:


public CompletableFuture<Data> getData() {

    final Data accumulator = new Data();

    CompletableFuture<Data> result = new CompletableFuture<>();

    CompletableStage exec = ... //starting execution

    exec.thenComposeAsync(
                    (res) -> process(accumulator, res)
            ).thenAccept(t -> result.complete(accumulator));
    return result;
  }

  private CompletionStage<Void> process(Data acc, Result res) {

    res.data().forEach(
            currData -> {
              add.addData(currData);
            }
    );
    if (res.hasMoreData()) {
      return res.fetchNextData().thenComposeAsync(
              (nextData) -> process(acc, nextData)
      );
    }

    return CompletableFuture.completedFuture(null);

  }

我不知道这是否是实现该解决方案的最佳方法,但如果一切正常,它会起作用。当由于某种原因在forEach块中出现异常时,问题就来了,该错误不会传播回getData调用者,因此我无法使用exceptionally方法捕获该错误以停止我的以安全的方式进行应用。我想我做错了。

1 个答案:

答案 0 :(得分:2)

如果传递给thenComposeAsync的函数因异常而失败,则thenComposeAsync返回的未来将异常完成。这导致由链式普通业务创建的期货也异常完成,而没有评估其功能。

该规则有三个例外,exceptionally仅在异常完成后才进行评估,以产生替换值,而handlewhenComplete在两种情况下均进行评估。

因此,当您要使用备用值替换异常时,可以使用

exec.thenComposeAsync(res -> process(accumulator, res))
    .exceptionally(throwable -> fallBack)
    .thenAccept(t -> result.complete(accumulator));

exceptionally之前必须特别注意将链thenAccept链接起来,否则在特殊情况下传递给thenAccept的函数将不会得到评估。

当您要将异常传播到result将来时,可以使用

exec.thenComposeAsync(res -> process(accumulator, res))
    .whenComplete((value, throwable) -> {
         if(throwable == null) result.complete(accumulator);
         else result.completeExceptionally(throwable);
    });

至关重要的是,将throwablenull进行比较,以确定完成是否出色,因为value可以作为null的普通结果。即使在普通结果值永远不能为null的情况下,也建议您遵循惯用的解决方案,因为您不知道是否以及何时重用代码。