简单的`CompletableFuture.exceptionally()`在异常完成时未被触发。

huangapple go评论66阅读模式
英文:

simple completableFuture.exceptionally() not triggered when completing exceptionally()

问题

我不理解一些基础的东西。

public static void main(String[] args) {
    CompletableFuture<String> cf = new CompletableFuture<>();
    cf = cf.exceptionally(throwable -> "inside exceptionally");

    cf.completeExceptionally(new IOException());
    String s = "im empty";
    try {
        s = cf.get();
    } catch (InterruptedException e) {
        System.out.println("inside InterruptedException catch");
    } catch (ExecutionException e) {
        System.out.println("inside ExecutionException catch");
    }
    System.out.println(s);
}

我预期此代码输出为:

> inside exceptionally

实际输出为:

> inside ExecutionException catch
> im empty

根据 .exceptionally 的 Java 文档:

返回一个新的 CompletableFuture,当这个 CompletableFuture 完成时,它的结果是由异常触发的给定函数的结果,当它异常完成时触发这个 CompletableFuture 的完成;否则,如果这个 CompletableFuture 正常完成,那么返回的 CompletableFuture 也会以相同的值正常完成。

如果是这样,为什么 get 的异常捕获被触发了呢?
我以为我通过异常方式完成它会触发 .exceptionally() 方法并给我一个有效的字符串响应...

我漏掉了什么?

英文:

I dont understand something basic.

public static void main(String[] args) {
    CompletableFuture&lt;String&gt; cf = new CompletableFuture&lt;&gt;();
    cf = cf.exceptionally(throwable -&gt;  &quot;inside exceptionally&quot;);

    cf.completeExceptionally(new IOException());
    String s = &quot;im empty&quot;;
    try {
        s = cf.get();
    } catch (InterruptedException e) {
        System.out.println(&quot;inside InterruptedException catch&quot;);
    } catch (ExecutionException e) {
        System.out.println(&quot;inside ExecutionException catch&quot;);
    }
    System.out.println(s);
}

what i expected this code to output =

> inside exceptionally

the actual output:

> inside ExecutionException catch

> im empty

from the .exceptionally javadoc:

> Returns a new CompletableFuture that is completed when this CompletableFuture completes, with the result of the given function of the exception triggering this CompletableFuture's completion when it completes exceptionally; otherwise, if this CompletableFuture completes normally, then the returned CompletableFuture also completes normally with the same value.

If so, why did the catch of the get triggered?
I assumed me completing it exceptionally will trigger the .exceptionally() method and give me a valid String response...

What am I missing?

答案1

得分: 0

在这个片段中,

CompletableFuture<String> cf = new CompletableFuture<>();
cf = cf.exceptionally(throwable ->  "inside exceptionally");

你正在创建一个 CompletableFuture,然后稍后打算完成它,并将其引用赋值给 cf。接着你调用的 exceptionally 返回一个新的 CompletableFuture 引用,你将其重新赋值给了 cf,导致你失去了原始的引用。

当你稍后调用

cf.completeExceptionally(new IOException());

你这样做的对象是由 exceptionally 返回的 CompletableFuture,而不是原始的那个。原始的

CompletableFuture<String> cf = new CompletableFuture<>();

仍然未完成。

最后,当你调用

s = cf.get();

你再次对由 exceptionally 返回的 CompletableFuture 进行操作,它被异常地完成了,带有一个 IOException。你会注意到,如果在 catch 块中调用

e.getCause();

它会包含那个 IOException


你正在链接 futures,但想要操作它们两个。你想要完成原始的并且从 exceptionally() 返回的那个获取结果。所以将它们都存储在各自的变量中并进行操作。例如,

CompletableFuture<String> root = new CompletableFuture<>();
CompletableFuture<String> dependent = root.exceptionally(throwable -> "inside exceptionally");

root.completeExceptionally(new IOException());
String s = "im empty";
try {
    s = dependent.get();
} catch (InterruptedException e) {
    System.out.println("inside InterruptedException catch");
} catch (ExecutionException e) {
    System.out.println("inside ExecutionException catch");
}
System.out.println(s);

这将打印出

inside exceptionally

意味着当 root CompletableFuture 异常完成时,传递给 exceptionallyFunction 会被执行。

英文:

In this snippet

CompletableFuture&lt;String&gt; cf = new CompletableFuture&lt;&gt;();
cf = cf.exceptionally(throwable -&gt;  &quot;inside exceptionally&quot;);

you're creating a CompletableFuture that you later intend to complete and assigning its reference to cf. The exceptionally you then call returns a new CompletableFuture reference that you assign back to cf and causing you to lose the original.

When you later call

cf.completeExceptionally(new IOException());

you're doing so on the CompletableFuture returned by exceptionally, not the original. The original

CompletableFuture&lt;String&gt; cf = new CompletableFuture&lt;&gt;();

remains incomplete.

Finally, when you call

s = cf.get();

you're again doing so on the CompletableFuture returned by exceptionally, which was completed exceptionally with an IOException. You'll notice that if you call

e.getCause();

in that catch block, it'll contain that IOException.


You're chaining futures, but want to operate on both of them. You want to complete the original and get the result from the the one returned by exceptionally(). So store both of them in their own variables and do your thing. For example,

CompletableFuture&lt;String&gt; root = new CompletableFuture&lt;&gt;();
CompletableFuture&lt;String&gt; dependent = root.exceptionally(throwable -&gt; &quot;inside exceptionally&quot;);

root.completeExceptionally(new IOException());
String s = &quot;im empty&quot;;
try {
    s = dependent.get();
} catch (InterruptedException e) {
    System.out.println(&quot;inside InterruptedException catch&quot;);
} catch (ExecutionException e) {
    System.out.println(&quot;inside ExecutionException catch&quot;);
}
System.out.println(s);

which will print

inside exceptionally

meaning the Function passed to exceptionally was executed when the root CompletableFuture was completed exceptionally.

huangapple
  • 本文由 发表于 2020年9月2日 21:57:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/63707136.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定