我已经习惯了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>
。< / p>
这意味着我不能只记录错误,我必须在错误情况下提出一个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库中的其他东西做些什么来更好地支持这个用例吗?
答案 0 :(得分:12)
使用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);
}
});
这里有趣的部分是你在你的例子中链接了未来。看似流畅的语法实际上是链接未来,但似乎你不想在这里。
whenComplete
返回的未来可能会很有意思,如果你想回到一个处理内部未来结果的未来。如果有的话,它会保留当前未来的例外情况。但是,如果将来正常完成并且继续抛出,则抛出异常会异常完成。
不同之处在于future
完成后发生的任何事情都会在下一次继续之前发生。如果您是exceptionally
的最终用户,则使用thenAccept
和future
是等效的,但如果您要将未来提供给调用方,则任何一方都将在未完成的情况下处理通知(如果在后台,如果可能的话),很可能是exceptionally
延续,因为您可能希望异常在进一步延续时级联。
答案 1 :(得分:2)
注意,exceptionally(Function<Throwable,? extends T> fn)
也会返回CompletableFuture<T>
。所以你可以进一步发展。
Function<Throwable,? extends T>
的返回值意味着为下一个链式方法生成回退结果。因此,您可以从缓存中获取值,如果它不可用于DB。
CompletableFuture<String> future = CompletableFuture<String>.supplyAsync(/*get from DB*/)
.exceptionally((t) -> {
log.error("Unexpected error", t);
return "Fallback value from cache";
})
.thenAccept(this::handleResult);
如果exceptionally
接受Supplier<T>
而不是功能,那么它如何返回CompletableFuture<String>
以进一步链接?
我想你想要一个exceptionally
的变体,它会返回void
。但不幸的是,不,没有这样的变种。
因此,在您的情况下,如果您不返回此future
对象并且不再在代码中使用它,那么您可以安全地从此回退函数返回任何值(因此它不能被进一步链接) 。最好不要将它分配给变量。
CompletableFuture<String>.supplyAsync(/*get from DB*/)
.thenAccept(this::handleResult)
.exceptionally((t) -> {
log.error("Unexpected error", t);
return null;
});