我经常发现返回 CompletableFuture 的库方法,并且函数签名中也有
throws Exception
。据我了解,这些函数中抛出的异常不会被 try-catch 捕获,因为 future 通常要等到代码离开 try-catch 块很久之后才会完成。因此,我们被告知将 .exceptionally()
附加到 CompletableFuture 链来处理这些失败。
所以我希望能够这样写:
CompletableFuture<Void> libFunction() throws Exception;
CompletableFuture<Void> caller() {
return libFunction()
.exceptionally(exception -> {
// code to handle exception
})
}
问题是 java 要求 caller() 在 libFunction 调用周围包含一个 try-catch 块,或者让 caller() 在其签名中包含
throws Exception
。如果我包含一个 try-catch,则相当于编写一些无用的异常处理代码,这些代码永远不会被调用,并且对于异常块中的代码来说是多余的。
另一个选项是将
throws Exception
添加到我的函数签名中。但这意味着调用链中的每个函数,直到调用 .join() 之前,都需要在其签名中包含 throws Exception
。而且事实上,该函数实际上永远不会抛出异常,因为如果抛出异常,CompletableFuture 会将异常包装在 CompletionException 中,并将其传递给 .exceptionally。
所以我的问题是如何使用 CompletableFuture 编写良好的异步代码,而不需要将不必要的异常添加到我的所有函数签名或冗余的 try-catch 块中以满足编译器的要求?
更新:更多上下文 - 我使用一个框架,该框架自动生成与 gRPC 服务路由相关的签名。这些签名本身返回 CompletionStage。因此从技术上讲,连接是由框架调用的,而不是由我调用。因此,由于顶级函数签名是自动生成的,因此仅将异常冒泡到发生连接的位置是不切实际的。
感谢@rogue 和@david-conrad 帮助我理解这一点。