Oui, chaque avenir sera tenté indépendamment pour être achevé.
Je pense que vous essayez également de comprendre comment le contrôle circule dans différents scénarios. J'ai imaginé 4 scénarios :
- Un futur où l'échec se produira à cause d'une exception non gérée.
- Un futur qui est explicitement marqué comme ayant échoué avec un completeExceptionally ET a un bloc exceptionnellement à sa queue.
- Un futur qui est explicitement marqué comme ayant échoué avec un completeExceptionally ET qui n'a pas de bloc exceptionnel à sa queue.
-
Un avenir qui s'achève sur un succès.
//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();
La sortie produite sur la console est
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
Comme vous pouvez le constater, si un futur génère une erreur non gérée, comme dans le cas 1, s'il a un fichier exceptionally
enchaîné à sa queue, l'exception sera traitée à ce moment-là
Comme pour le cas 2, dans le cas où le futur est marqué comme échoué avec completeExceptionally
si le futur a un gestionnaire enchaîné à sa queue, alors la fonction exceptionally
sera traité par ce bloc
Dans le cas 3, le futur est marqué comme échoué et n'a pas de bloc d'exception, il sera donc traité par la fonction exceptionally
au niveau suivant, dans ce cas, il s'agit du bloc exceptionally
du bloc allOf()
.
Comme vous pouvez le voir, le cas 4 s'exécute jusqu'au bout et le message s'affiche sur la console, indépendamment des échecs des autres futurs.