这个问题是针对Java和< code>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()
并返回它返回的字符串长度:
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 */
}
}
我的问题是:
< code>get()写得不好是因为它要求其调用方进行这种复杂的异常处理,还是这是一种常见的模式?返回< code>CompletableFuture的异步方法是否应该局限于在出错时返回出错的未来,以便它们的调用方不必编写这样的错误处理代码?
简而言之,这取决于您自己的实现,但它可以改进。如果您想忽略、记录异常,或者通过不同的线程对异常做出反应,那么让调用线程处理异常可能会很有用(这里是基本示例)。然而,我看到的许多模式(见本文答案)都会让您使用try-catch块
包含async
function,并重新抛出一个对您的应用程序更有用的异常,由父线程处理,我认为这会更好一些。
如果您正在寻找不同的异常处理,请参阅本文以获取不同处理的示例。
我认为为参数验证抛出异常是合适的,因为调用方不应该处理这些异常——非法参数是一个必须修复的bug。
但是,最好将其他异常放在返回的CompletableFuture
中,以便调用者可以使用标准的CompletableFuture
异常处理和链接。同样,您不会返回null
未来,而是返回用null
完成的未来。另请参阅已完成的CompletableFuture,但有异常。