【问题标题】:Using Java `CompletablyFuture.exceptionally` to optionally continue through exceptions使用 Java `CompletablyFuture.exceptionally` 选择性地继续处理异常
【发布时间】:2017-10-31 21:44:55
【问题描述】:

我有一系列使用 Java CompletableFutures 链接在一起的异步任务。代码如下所示:

CompletableFuture<Result> doTasks(final TaskId id) {
    return firstTask.workAsync(id)
            .thenComposeAsync(__ -> secondTask.workAsync(id))
            .thenComposeAsync(__ -> thirdTask.workAsync(id))
            .thenApplyAsync(__ -> fourthTask.workAsync(id));
}

但是,firstTask.workAsync 会抛出一个异常,表示工作已经完成,在这种情况下是可以的,所以我想直接忽略它并继续遍历链。

当然,我可以将那个位包装在一个单独的函数中,在那里我可以处理异常,但是有没有办法直接在CompletableFuture 链中处理它并继续抛出所有其他异常?

一位同事建议我使用CompletableFuture.exceptionally,但是我在网上看到的所有示例都完全没用,只是返回null,这看起来会杀死链条。在这种情况下我将如何使用它?

【问题讨论】:

    标签: java asynchronous concurrency completable-future


    【解决方案1】:

    CompletableFuture.exceptionally 可用于在CompletableFuture 中获取异常时继续。简而言之,你需要检查异常的类型,如果是你想继续处理的异常,你可以返回一个新的CompletableFuture,它可以为空,因为结果没有被沿链使用。

    CompletableFuture<Result> doTasks(final TaskId id) {
        return firstTask.workAsync(id)
                .exceptionally(t -> {
                    // Will continue down the chain if matches
                    if (t instanceof TotallyOkException) {
                        return null;
                    }
    
                    // This will throw an ExecutionException. I convert it to a RuntimeException here 
                    // because I don't want to add throws statements up the chain.
                    throw new RuntimeException(t);
                })
                .thenComposeAsync(__ -> secondTask.workAsync(id))
                .thenComposeAsync(__ -> thirdTask.workAsync(id))
                .thenApplyAsync(__ -> fourthTask.workAsync(id));
    }
    

    在这种情况下,它会抛出所有非TotallyOkException 的异常。

    【讨论】:

    • 我猜你的意思是return null而不是return CompletableFuture.completedFuture(null)
    • @JoeC 不会返回 null 杀死链吗?它按预期工作,但如果我希望任务 2-4 仍然运行,我不需要返回 CompletableFuture 吗?
    • 这是签名:exceptionally(Function&lt;Throwable,? extends T&gt; fn)。所以这只有在你有CompletableFuture&lt;CompletableFuture&lt;T&gt;&gt; 时才有效。没有理由返回 null 应该停止链。
    • @JoeC 我明白了。我将它切换为返回null,它仍然按预期工作,所以你是对的。我相应地更新了答案。发送。
    【解决方案2】:

    在您的 exceptionally 函数中返回 null 本身不会杀死链。它会杀死链的唯一方法是下游函数中缺少空处理并导致NullPointerException

    您的exceptionally 函数可以设置为处理某些类型的异常,而不是其他类型。例如:

    return firstTask.workAsync(id)
            .thenComposeAsync(firstResult -> secondTask.workAsync(id))
            .exceptionally(t -> {
                               if (t instanceof TransientException) {
                                   return getUsingBackupMethod(id);
                               }
                               throw new RuntimeException(t);
                           });
    

    这个exceptionally 函数将(有效地)捕获前两个任务中的任何一个引发的异常。

    【讨论】:

      猜你喜欢
      • 2019-01-15
      • 2019-09-05
      • 2010-09-13
      • 2015-01-03
      • 1970-01-01
      • 1970-01-01
      • 2012-04-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多