java 传递给 CompletableFuture.exceptionally() 的异常处理程序是否必须返回一个有意义的值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37770539/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 02:46:52  来源:igfitidea点击:

Does an exception handler passed to CompletableFuture.exceptionally() have to return a meaningful value?

javacompletable-future

提问by David Moles

I'm used to the ListenableFuturepattern, with onSuccess()and onFailure()callbacks, e.g.

我习惯了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);
  }
})

It seems like Java 8's CompletableFutureis meant to handle more or less the same use case. Naively, I could start to translate the above example as:

似乎 Java 8CompletableFuture旨在处理或多或少相同的用例。天真地,我可以开始将上面的例子翻译为:

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

This is certainly less verbose than the ListenableFutureversion and looks very promising.

这肯定没有ListenableFuture版本那么冗长,看起来非常有前途。

However, it doesn't compile, because exceptionally()doesn't take a Consumer<Throwable>, it takes a Function<Throwable, ? extends T>-- in this case, a Function<Throwable, ? extends String>.

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

This means that I can't just log the error, I have to come up with a Stringvalue to return in the error case, and there is no meaningful Stringvalue to return in the error case. I can return null, just to get the code to compile:

这意味着我不能只记录错误,我必须想出一个String值在错误情况下返回,而在错误情况下没有有意义的String值返回。我可以返回null,只是为了让代码编译:

  .exceptionally((t) -> {
    log.error("Unexpected error", t);
    return null; // hope this is ignored
  });

But this is starting to get verbose again, and beyond verbosity, I don't like having that nullfloating around -- it suggests that someone might try to retrieve or capture that value, and that at some point much later I might have an unexpected NullPointerException.

但这又开始变得冗长了,除了冗长之外,我不喜欢null让它四处飘荡——这表明有人可能会尝试检索或捕获该值,而在很久以后的某个时刻,我可能会遇到一个意想不到的NullPointerException.

If exceptionally()took a Function<Throwable, Supplier<T>>I could at least do something like this --

如果exceptionally()拿了,Function<Throwable, Supplier<T>>我至少可以做这样的事情——

  .exceptionally((t) -> {
    log.error("Unexpected error", t);
    return () -> { 
      throw new IllegalStateException("why are you invoking this?");
    }
  });

-- but it doesn't.

——但事实并非如此。

What's the right thing to do when exceptionally()should never produce a valid value? Is there something else I can do with CompletableFuture, or something else in the new Java 8 libraries, that better supports this use case?

exceptionally()永远不应该产生有效值时,正确的做法是什么?有什么我可以做的CompletableFuture,或者新的 Java 8 库中的其他东西,可以更好地支持这个用例吗?

采纳答案by acelent

A correct corresponding transformation with CompletableFutureis:

一个正确的对应变换CompletableFuture是:

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

Another way:

其他方式:

CompletableFuture<String> future = CompletableFuture.supplyAsync(...);
future
    .whenComplete((r, t) -> {
        if (t != null) {
            log.error("Unexpected error", t);
        }
        else {
            this.handleResult(r);
        }
    });

The interesting part here is that you were chaining futures in your examples. The seemingly fluent syntax is actually chaining futures, but it seems you don't want that here.

这里有趣的部分是您在示例中链接了期货。看似流畅的语法实际上是链接期货,但似乎您不想要这里。

The future returned by whenCompletemight be interesting if you want to return a future that processes something with an internal future's outcome. It preserves the current future's exception, if any. However, if the future completed normally and the continuation throws, it'll complete exceptionally with the thrown exception.

通过返回的未来whenComplete,如果你想返回一个未来可能是有趣的是过程的东西与内部未来的结果。它保留当前未来的异常,如果有的话。但是,如果future 正常完成并且continuation 抛出,它将异常完成并抛出异常。

The difference is that anything that happens after futurecompletes will happen before the next continuation. Using exceptionallyand thenAcceptis equivalent if you're the future's end-user, but if you're providing a future back to a caller, either one will process without a completion notification (as if in the background, if you may), most probably the exceptionallycontinuation since you'll probably want the exception to cascade on further continuations.

不同之处在于,在future完成之后发生的任何事情都会在下一个延续之前发生。如果您是最终用户,则使用exceptionallyandthenAccept是等效的future,但是如果您向调用者提供未来返回,则任何一个都将在没有完成通知的情况下进行处理(如果可以的话,就像在后台一样),很可能的exceptionally,因为你延续可能会想例外级联的进一步延续。

回答by Ruslan Stelmachenko

Note, that exceptionally(Function<Throwable,? extends T> fn)also returns CompletableFuture<T>. So you can chain futher.

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

The return value of Function<Throwable,? extends T>is meant to produce fallback result for next chained methods. So you can for example get the value from Cache if it is unavailable from DB.

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

CompletableFuture<String> future = CompletableFuture<String>.supplyAsync(/*get from DB*/)
  .exceptionally((t) -> {
    log.error("Unexpected error", t);
    return "Fallback value from cache";
  })
  .thenAccept(this::handleResult);

If exceptionallywould accept Supplier<T>instead of function, then how it could return a CompletableFuture<String>for chaining futher?

如果exceptionally将接受Supplier<T>而不是函数,那么它如何返回 aCompletableFuture<String>以进一步链接?

I think you want a variant of exceptionallywhich would return void. But unfortunately, no, there is no such variant.

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

So, in your case you can safely return any value from this fallback function, if you not return this futureobject and not use it futher in your code (so it can't be chained futher). Better not even assign it to a variable.

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

CompletableFuture<String>.supplyAsync(/*get from DB*/)
  .thenAccept(this::handleResult)
  .exceptionally((t) -> {
    log.error("Unexpected error", t);
    return null;
  });