这个问题是特定于Java和CompletableFuture
的。
如果我有一个像下面这样的异步方法,
CompletableFuture<String> get() {
/* Step #1: Do some sync prep work */
String s = doSomethingSync();
/* Step #2: Do something async (returning CompletableFuture) */
return doSomethingAsync(s);
}
如果步骤 #1 中的代码抛出,则get()
的调用方将在获取返回CompletableFuture
之前获得异常,而如果在步骤 #2 中返回CompletableFuture
中的代码,则调用方只有在与返回的CompletableFuture
交互时才会收到异常。
这表明get()
的调用方应该编写一些复杂的异常处理代码来解决这两种情况。
下面是另一个异步方法invokeGet()
的示例,该方法调用get()
并返回它返回的String
的长度:
CompletableFutre<Integer> InvokeGet() {
try {
CompletableFuture future = get();
return furure.handle((result, throwable) -> {
if (throwable != null) {
/* Handle exception thrown in step #2 in get(), e.g. rethrow */
} else {
return result.length();
}
});
} catch (Exception e) {
/* Handle exception thrown in step #1 in get() */
/* Return some value or throw */
}
}
我的问题是:
get()
写得不好,因为它需要调用方执行这种复杂的异常处理,还是这是一种常见且常见的模式?异步方法CompletableFuture
是否应该在发生错误时返回错误的未来,以便它们的调用方不必编写这样的错误处理代码?
简而言之,这取决于您自己的实现,但可以改进。在您希望通过其他线程忽略、记录或响应异常的情况下,让调用线程处理异常可能会有所帮助(此处的基本示例(。但是,我看到的很多模式(在这篇文章中看到(会让你包含带有try-catch-block
的async
函数,并重新抛出一个对你的应用程序更有用的异常,由父线程处理,我认为这更好一点。
如果要查找不同的异常处理,请参阅本文以获取不同处理的示例。
我认为为参数验证抛出异常是合适的,因为调用者不应该处理这些异常——非法参数是一个必须修复的错误。
但是,最好将其他异常放在返回的CompletableFuture
中,以便调用者可以使用标准的异常处理和链接CompletableFuture
。同样,你不会返回一个null
的未来,而是一个用null
完成的未来。另请参阅CompletableFuture已经完成,但有例外。