foo.thenCompose(fooResponse -> {
...
return bar.thenCompose(barResponse -> {
...
});
}).exceptionally(e -> {
...
});
这个.exceptionally()
是否也会捕获嵌套的bar.thenCompose
lambda中抛出的异常?或者我需要写这个:
foo.thenCompose(fooResponse -> {
...
return bar.thenCompose(barResponse -> {
...
}).exceptionally(nestedE -> {
...
});
}).exceptionally(e -> {
...
});
然后重新抛出?
最后一个exceptionally
足以用替代的普通结果值替换任何throwable,至少对于它返回的结果阶段,但是值得清理导致这个问题的思维模式。
exceptionally
没有任何例外情况,也没有嵌套期货。重要的是要理解CompletionStage
定义的所有方法都会创建一个新的完成阶段,其完成将受特定方法的契约影响,但从不影响完成阶段,该方法已被调用。
因此,当你使用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
将永远不会被完成并且函数永远不会被评估,因此,“嵌套”阶段将永远不会被创建,但正如所说,这段历史与stage2
无关。
因此,如果您最后一次调用链接完成阶段是exceptionally(function)
,它将返回一个新阶段,该阶段将始终使用前一阶段或从function
返回的值完成,而不管它们之前的依赖关系图如何。除非function
本身抛出一个异常,否则有人会调用其中一个明确的完成方法,比如cancel
。
是的,通过“特殊”方法,您可以处理嵌套的CompletableFuture第一个示例将正常工作