从返回 CompletableFuture 的异步方法的同步部分引发异常



这个问题是特定于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-blockasync函数,并重新抛出一个对你的应用程序更有用的异常,由父线程处理,我认为这更好一点。

如果要查找不同的异常处理,请参阅本文以获取不同处理的示例。

我认为为参数验证抛出异常是合适的,因为调用者不应该处理这些异常——非法参数是一个必须修复的错误。

但是,最好将其他异常放在返回的CompletableFuture中,以便调用者可以使用标准的异常处理和链接CompletableFuture。同样,你不会返回一个null的未来,而是一个用null完成的未来。另请参阅CompletableFuture已经完成,但有例外。

最新更新