【问题标题】:CompletableFuture exceptionally() and handle() swallowing RuntimeException?CompletableFuture exceptionly() 和 handle() 吞下 RuntimeException?
【发布时间】:2020-07-13 00:08:58
【问题描述】:

我有以下几点:

cflst.addAll(asList(
            supplyAsync(() -> mytask.getComp("comp"), executor)
                    .thenAccept(comp -> helper.mapStuff(comp))
                    .exceptionally(ex -> {
                        sout("Some Error");  // Never print
                        return null;
                    })
                    .handle((comp, throwable) -> {
                        sout("Error here" + comp); //always prints regarless of exception
                        return null;
                    }),

    ...

    ));

 CompletableFuture.allOf(cflst.toArray(new CompletableFuture[0])).join();

还有MyTask.getComp方法

 CompX getComp(String id) {
     client.getData(id);
  }

Client.getData()

 SomeX getData(String id) {
    throw new MyCustomRuntimeException("Blew Up");
 }
  

我认为当myComp 方法中抛出异常时,exceptionally() 块得到了异常。但似乎没有。

另外,不管有什么异常,handle() 总是被调用 !?!?!

我希望能够捕获任何异常并根据需要进行处理(重新抛出或吞下)。

更新

当我在getComp 方法中捕获异常然后重新抛出它时,exceptionally 块被执行。但不知道为什么当异常在getData 方法向下抛出一个级别时它没有被执行?

【问题讨论】:

  • getCommp() 是您帖子中的错字吗? (始终复制/粘贴代码,切勿重新输入)
  • 是的。修好了。
  • handle() 总是被调用不是问题,这是可以预料的;它应该打印“此处的错误:null”,因为应该事先调用 exceptionally() 并将结果设置为 null。问题是为什么exceptionally中的消息没有输出; sout() 是什么,也许它以某种方式抑制了它?
  • 我看到你已经接受了一个答案,到底是什么问题?答案没有解释为什么你的exceptionally() 函数没有被执行……
  • 关于“从不打印”问题。我在 Android 中看到了类似的问题,在 .exceptionally 中有一个 Toast。在 UI 线程上运行的块内的相同 Toast 工作正常。通过检查日志,当我检查它时,异常总是出现在不同的线程上。它并不像看起来那么简单,但是因为 Toast 似乎无声地崩溃并且在它之后没有异常执行的代码,但似乎没有其他执行受到影响,而且在 UI 中更新 TextView 异常工作正常。尚未深入了解,但好奇您是否找到了“从不打印”问题的根源?

标签: java multithreading asynchronous exception completable-future


【解决方案1】:

在您的示例中,您按顺序编写 CompletableFutures。

.exceptionally() 之后,您会得到一个产生null 而不是抛出异常的未来。因此,后续的.handle() 永远不会观察到异常。

如果您希望.handle() 获得相同的异常,则必须在调用.exception() 的同一个将来调用它。例如,使用一个变量来存储您在.thenAccept() 之后得到的未来,然后在其上组合.exceptionally().handle(),而不是顺序。

但是,您应该在.exceptionally().handle() 之间进行选择:

  • .exceptionally() 通常用于返回默认值,而不是让异常通过

  • .handle() 通常用于记录、转换和/或包装异常,如catch 块;如果您希望异常通过,请不要忘记重新抛出

对于像finally 块这样的清理操作,.whenComplete() 更合适。

【讨论】:

  • OP的问题不是handle没有得到异常,而是只有handle得到了异常而exceptionally根本没有被调用。
  • OP 更新说直接从getComp() 抛出的异常经过exceptionally()。我无法复制 OP 的观察结果。正如您在问题中直接评论的那样,也许sout() 正在做某事,比如投掷。尽管如此,我还是决定投稿,描述exceptionallywhenCompletehandle 之间的选择。
【解决方案2】:

handle()方法创建的stage不管有没有异常都会执行,而exceptionally()stage只有在有异常的时候才会执行。

我无法重现您的错误,只要未捕获到异常,exceptionally 块就会为我执行。

在我看来,您应该使用handleexceptionally

handle() 方法使您能够为异常提供默认值,exceptionally() 方法类似于句柄,但不那么冗长

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    相关资源
    最近更新 更多