21

我习惯了这种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 8CompletableFuture旨在处理或多或少相同的用例。天真地,我可以开始将上面的示例翻译为:

CompletableFuture<String> future = CompletableFuture<String>.supplyAsync(...)
  .thenAccept(this::handleResult)
  .exceptionally((t) -> log.error("Unexpected error", t));

这肯定没有ListenableFuture版本那么冗长,而且看起来很有希望。

但是,它不会编译,因为exceptionally()不需要 a Consumer<Throwable>,它需要 a Function<Throwable, ? extends T>-- 在这种情况下是 a Function<Throwable, ? extends String>

这意味着我不能只记录错误,我必须想出一个String在错误情况下返回的值,而在错误情况下没有任何有意义的String值可以返回。我可以 return 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 库中的其他东西,可以更好地支持这个用例吗?

4

2 回答 2

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完成后发生的任何事情都将在下一个延续之前发生。如果您是的最终用户,则使用exceptionallyand是等效的,但是如果您向调用者提供未来返回,则任何一个都将在没有完成通知的情况下进行处理(如果可能的话,就好像在后台一样),很可能延续,因为您可能希望异常在进一步的延续上级联。thenAcceptfutureexceptionally

于 2016-06-13T14:55:54.860 回答
3

请注意,这exceptionally(Function<Throwable,? extends T> fn)也返回CompletableFuture<T>. 所以你可以进一步链接。

的返回值Function<Throwable,? extends T>旨在为下一个链接方法产生回退结果。因此,例如,如果数据库中不可用,您可以从缓存中获取值。

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接受Consumer<T>而不是函数,那么它如何CompletableFuture<String>进一步返回一个用于链接?

我想你想要一个变体,exceptionally它会返回void。但不幸的是,不,没有这样的变体。

future因此,在您的情况下,如果您不返回此对象并且不在您的代码中进一步使用它(因此不能进一步链接),您可以安全地从此回退函数返回任何值。最好不要将其分配给变量。

CompletableFuture<String>.supplyAsync(/*get from DB*/)
  .thenAccept(this::handleResult)
  .exceptionally((t) -> {
    log.error("Unexpected error", t);
    return null;
  });
于 2016-06-12T04:30:48.560 回答