我习惯了 ListenableFuture
模式,使用onSuccess()
和onFailure()
回调,例如
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
ListenableFuture<String> future = service.submit(...)
Futures.addCallback(future, new FutureCallback<String>() {
public void onSuccess(String result) {
handleResult(result);
}
public void onFailure(Throwable t) {
log.error("Unexpected error", t);
}
})
似乎Java 8的
CompletableFuture
旨在处理或多或少相同的用例。天真的,我可以将上面的示例翻译为:CompletableFuture<String> future = CompletableFuture<String>.supplyAsync(...)
.thenAccept(this::handleResult)
.exceptionally((t) -> log.error("Unexpected error", t));
这肯定比
ListenableFuture
版本更详细,而且看起来很有希望。但是,它不会编译,因为
exceptionally()
不采用Consumer<Throwable>
,因此它采用Function<Throwable, ? extends T>
-在这种情况下为Function<Throwable, ? extends String>
。这意味着我不能只记录错误,我必须想出一个
String
值以在错误情况下返回,并且没有有意义的String
值以在错误情况下返回。我可以返回null
,仅用于编译代码: .exceptionally((t) -> {
log.error("Unexpected error", t);
return null; // hope this is ignored
});
但这又开始变得冗长,除了冗长之外,我不喜欢让
null
随处可见-这表明有人可能会尝试检索或捕获该值,并且在以后的某个时刻,我可能会遇到意外NullPointerException
。如果
exceptionally()
接受了Function<Throwable, Supplier<T>>
,我至少可以做这样的事情- .exceptionally((t) -> {
log.error("Unexpected error", t);
return () -> {
throw new IllegalStateException("why are you invoking this?");
}
});
-但事实并非如此。
当
exceptionally()
永远不会产生有效值时,该怎么办?我可以使用CompletableFuture
进行其他操作,或者使用新Java 8库中的其他方法来更好地支持此用例吗? 最佳答案
正确的CompletableFuture
对应转换是:
CompletableFuture<String> future = CompletableFuture.supplyAsync(...);
future.thenAccept(this::handleResult);
future.exceptionally(t -> {
log.error("Unexpected error", t);
return null;
});
其它的办法:
CompletableFuture<String> future = CompletableFuture.supplyAsync(...);
future
.whenComplete((r, t) -> {
if (t != null) {
log.error("Unexpected error", t);
}
else {
this.handleResult(r);
}
});
这里有趣的部分是您在示例中链接了 future 。看似流利的语法实际上是在链接 future ,但似乎您不希望在这里。
如果您想返回一个处理带有内部 future 结果的东西的 future ,则
whenComplete
返回的 future 可能会很有趣。它保留当前将来的异常(exception)(如果有)。但是,如果将来正常完成并且引发续集,那么它将以异常抛出异常完成。区别在于
future
完成后发生的所有事情都将在下一个继续之前发生。如果您是exceptionally
的最终用户,则使用thenAccept
和future
是等效的,但是如果要向调用者提供将来的服务,则任何一个都将在没有完成通知的情况下进行处理(就像在后台,如果可能的话) ,很可能是exceptionally
延续,因为您可能希望异常在进一步的延续上级联。