foo.thenCompose(fooResponse -> {
...
return bar.thenCompose(barResponse -> {
...
});
}).exceptionally(e -> {
...
});
此.exceptionally()
还会捕获嵌套bar.thenCompose
lambda中引发的异常吗?还是我需要这样写:
foo.thenCompose(fooResponse -> {
...
return bar.thenCompose(barResponse -> {
...
}).exceptionally(nestedE -> {
...
});
}).exceptionally(e -> {
...
});
然后重新抛出?
答案 0 :(得分:4)
最后一个exceptionally
足以用可替代的普通结果值替换任何throwable,至少对于它返回的结果阶段而言,但这值得清理引发此问题的思维定势。
exceptionally
不会捕获任何异常,并且也没有嵌套期货。重要的是要了解,CompletionStage
定义的所有方法都创建了一个 new 完成阶段,该完成阶段将受特定方法的合同影响,但从不影响完成阶段,该方法已经调用。
因此,当您使用exceptionally
时,涉及到两个期货,一个是您特别调用的期货,另一个是exceptionally
返回的新期货。合同规定,在通常完成的情况下,后者的价值将与前者相同,但如果前者已经完成,则具有功能评估的结果。
所以当您执行
for(int run = 0; run < 4; run++) {
CompletableFuture<String> stage1 = new CompletableFuture<>();
CompletableFuture<String> stage2 = stage1.exceptionally(t -> "alternative result");
if(run > 1) stage2.cancel(false);
if((run&1) == 0) stage1.complete("ordinary result");
else stage1.completeExceptionally(new IllegalStateException("some failure"));
stage1.whenComplete((v,t) ->
System.out.println("stage1: "+(t!=null? "failure "+t: "value "+v)));
stage2.whenComplete((v,t) ->
System.out.println("stage2: "+(t!=null? "failure "+t: "value "+v)));
System.out.println();
}
它将打印:
stage1: value ordinary result
stage2: value ordinary result
stage1: failure java.lang.IllegalStateException: some failure
stage2: value alternative result
stage1: value ordinary result
stage2: failure java.util.concurrent.CancellationException
stage1: failure java.lang.IllegalStateException: some failure
stage2: failure java.util.concurrent.CancellationException
表明第一阶段总是反映我们明确完成的结果,而不管第二阶段发生了什么。因此exceptionally
不会捕获异常,上一阶段的特殊完成永远不会改变,它所做的只是定义新阶段的完成。
因此,如果stage1
是stage0.thenCompose(x -> someOtherStage)
通话的结果,则对stage1
和stage2
之间的关系并不重要。重要的是stage1
的完成。
stage0
异常完成,它将尝试异常完成stage1
stage0
用一个值完成,并且该函数引发异常,它将尝试异常完成stage1
stage0
用一个值完成,并且该函数返回一个已经或将要异常完成的阶段(someOtherStage
),它将尝试以异常方式完成stage1
stage0
用一个值完成,并且该函数返回一个已经或将要用一个值完成的阶段(someOtherStage
),它将尝试用该值完成stage1
值请注意,没有嵌套,someOtherStage
可能是新建的或已经存在的阶段,它也可能在其他地方使用。由于链接总是构建不影响现有阶段的新阶段,因此这些其他地方不会受到此处发生的任何事情的影响。
请进一步注意术语“尝试完成”,因为我们仍可以在尝试之前在complete
上调用completeExceptionally
,cancel
或stage1
。对于stage2
来说,完成的方式与结果无关紧要。
因此,如果从1.到3.的任何一种情况的尝试异常成功地完成stage1
都成功,则将尝试完成stage2
并将函数结果传递给exceptionally
。在情况4中,如果尝试成功完成一个值的stage1
,将尝试尝试完成该值的stage2
。
如果我们使用
,则可以证明前一阶段的历史无关紧要。CompletableFuture<String> stage1 = new CompletableFuture<>();
CompletableFuture<String> stage2 = stage1.thenCompose(s -> new CompletableFuture<>());
CompletableFuture<String> stage3 = stage2.exceptionally(t -> "alternative result");
stage1.complete("ordinary result"); // you can omit this line if you want
stage2.completeExceptionally(new IllegalStateException("some failure"));
stage3.whenComplete((v,t) ->
System.out.println("stage3: "+(t!=null? "failure "+t: "value "+v)));
它将打印stage3: value alternative result
,这是由于stage2
已异常完成,而完成的历史完全不相关。 stage1.complete("ordinary result");
语句将导致函数评估返回一个新的CompletableFuture
,它将永远不会完成,因此不会对结果有所帮助。如果我们省略此行,stage1
将永远不会完成,函数也不会被评估,因此,“嵌套”阶段将永远不会创建,但是正如所说的,此历史记录对{{1}而言并不重要}。
因此,如果您最后一次链接完成阶段的调用是stage2
,它将返回一个新阶段,该阶段将始终以上一个阶段或从exceptionally(function)
返回的值来完成,不管它们之前的依赖图是什么样子。除非function
本身抛出异常,否则除非有人调用它上的一种显式完成方法,例如function
。
答案 1 :(得分:0)
是的,通过“ exceptionally”方法,您可以处理嵌套的CompletableFuture 第一个示例将正常工作