I thought I understood whenComplete
but I'm not sure now. This question originated in another thread.
The way we work with futures in my company is by chaining them:
CompletionStage<Foo> getFoo() {
// ...
return barService.getBar()
.thenCompose(bar -> {
CompletionStage<Baz> baz = bazService.getBaz(bar);
// ...
return qux;
})
.thenApply(qux -> {
CompletionStage<Quux> quux = quuxService.getQuux(qux);
// ...
return foo;
});
}
qux and quux are apparently the metasyntactic variables that follow foo, bar, and baz.
Now let's say I wanted to send a confirmation email when foo
has been gotten. I don't need the sending of this confirmation email to hold up the response to whatever client called getFoo
. We use whenComplete
for these scenarios:
CompletionStage<Foo> getFoo() {
// ...
return barService.getBar()
.thenCompose(bar -> {
CompletionStage<Baz> baz = bazService.getBaz(bar);
// ...
return qux;
})
.thenApply(qux -> {
CompletionStage<Quux> quux = quuxService.getQuux(qux);
// ...
return foo;
}) _
.whenComplete((foo, ex) -> {. |
if (ex == null) { |
emailService.sendEmail(foo); | (NEW)
} |
}); _|
}
Now I thought the action in whenComplete
happened in a separate thread completely independently of the thread it originated from. In other words, I thought as soon as foo
was found, it'd be on its way to the caller, no matter what happened inside the whenComplete
. But in reality, when the email service had a problem and threw an exception, the exception propagated all they way up, i.e. getFoo
threw an exception, even though foo
was found succesfully.
I was pointed to the Javadoc for whenComplete
, which indeed says:
Unlike method handle, this method is not designed to translate completion outcomes, so the supplied action should not throw an exception. However, if it does, the following rules apply: if this stage completed normally but the supplied action throws an exception, then the returned stage completes exceptionally with the supplied action's exception. Or, if this stage completed exceptionally and the supplied action throws an exception, then the returned stage completes exceptionally with this stage's exception.
So here's where I'm confused:
I thought the whole point of whenComplete
was to allow the originating thread to continue on its way without having to wait for the action in whenComplete
. If whether or not the chain will complete normally depends on the whenComplete
action though, doesn't that mean the chain always has to wait to see how whenComplete
completes? How is whenComplete
helping at all, if that's true?
I'm sure that I'm thinking of something wrong / misunderstanding how futures work, but I don't know what.