So I have a couple futures which I want to run, even if some fail I'd like all to have a chance to run. So if I do:
CompletableFuture.allOf(futures).join()
Will that be the case? My reasoning is that every future would have its own queed job in its executor and therefore all would run provided the main thread doesn't finish first. My issue is that I specifically .join()
on .allOf()
so my application doesnt end before running everything
So allOf()
semantics confuse me: Will the future return complete when all of the passed futures complete regardless if successful? Or will it complete a failed future if it sees one failed without waiting for the rest?
EDIT
To illustrate my question further, does .allOf
behaves like this:
Stream.of(futures).forEach(future -> {
try {
future.join()
} catch (Throwable e) {
//dont throw, we want to join the rest
}
})
Or does it behaves like the following:
Stream.of(futures).forEach(future -> {
try {
future.join()
} catch (Throwable e) {
throw e; //All other remaining .join() wont run
}
})
Which is it? first or second case? Since I want the first case thats what I'm using on my code temporarily, but I'd like to use allOf()
if possible because its more aesthetic
Thanks!
Yes, each future will be independently attempted for completion.
I think you are also trying to understand how the control flows in various scenarios. I have come up with 4 scenarios :
//CASE 1
// A future that shall fail due to an unandled exception in its run
// and has an exceptionally block at its tail
CompletableFuture<Void> unhandledFailureFutureWithExceptionHandler =
CompletableFuture.runAsync(() -> {
throw new RuntimeException("Exception in unhandledFailureFutureWithExceptionHandler");
});
unhandledFailureFutureWithExceptionHandler = unhandledFailureFutureWithExceptionHandler
.exceptionally(throwable -> {
// Handling exception for this future
// HANDLING POINT 1
System.out.println("Handling exception at HANDLING POINT FOR CASE 1,
failure message is : " + throwable.getMessage());
return null;
});
//CASE 2
//A future that shall fail and has an exceptionally block at its tail
CompletableFuture<Void> failedFutureWithExceptionHandler = new CompletableFuture<>();
failedFutureWithExceptionHandler.completeExceptionally(
new RuntimeException("Exception in failedFutureWithExceptionHandler")
);
failedFutureWithExceptionHandler = failedFutureWithExceptionHandler.exceptionally(throwable -> {
// Handling exception for this future
// HANDLING POINT 2
System.out.println("Handling exception at HANDLING POINT FOR CASE 2,
failure message is : " + throwable.getMessage());
return null;
});
//CASE 3
//A future that shall fail and has no exceptionally block at its tail
CompletableFuture<Void> failedFutureWithoutExceptionHandler = new CompletableFuture<>();
failedFutureWithoutExceptionHandler.completeExceptionally(
new RuntimeException("Exception in failedFutureWithoutExceptionHandler")
);
//CASE 4
//A future that shall succeed and print a message to console
CompletableFuture<Void> successFuture = CompletableFuture.runAsync(() ->
System.out.println("CASE 4 : Running successFuture")
);
CompletableFuture.allOf(unhandledFailureFutureWithExceptionHandler,
failedFutureWithExceptionHandler, failedFutureWithoutExceptionHandler, successFuture)
.exceptionally(throwable -> {
// Handling exception if ANY of the futures that did not have its own exceptionally block
// In this case the exception of `failedFutureWithoutExceptionHandler` will be handled here
// HANDLING POINT 3
System.out.println("Handling exception at HANDLING POINT FOR CASE 3,
failure message is : " + throwable.getMessage());
return null;
}).join();
The output produced on the console is
Handling exception at HANDLING POINT FOR CASE 1, failure message is : java.lang.RuntimeException: Exception in unhandledFailureFutureWithExceptionHandler
Handling exception at HANDLING POINT FOR CASE 2, failure message is : Exception in failedFutureWithExceptionHandler
CASE 4 : Running successFuture
Handling exception at HANDLING POINT FOR CASE 3, failure message is : java.lang.RuntimeException: Exception in failedFutureWithoutExceptionHandler
As you can see if a future throws an unhandled error as in case 1, if it has a exceptionally
block chained to its tail, the exception shall be handled at that point
As for case 2, in case where the future is marked as failed with completeExceptionally
, if the future has a handler chained to its tail, then the exceptionally
block shall be handled by that block
In case 3, the future is marked as failed and does not have an exceptionally block, thus it shall be handled by the exceptionally
block at the next level, in this case it is the exceptionally
block of the allOf()
.
As you can see, case 4 runs to completion and the message gets print on the console irrespective of the failures of the other futures.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With