Estoy tratando de llegar a un CompletableFuture con los efectos combinados de whenComplete
y thenCompose
, específicamente:
- Devuelve un
CompletionStage
lugar de sólo un resultado, similar athenCompose
. - Ejecuta incluso cuando se complete la etapa anterior con carácter excepcional, similares a
whenComplete
, y no se detiene la excepción se propague.
Este post está cerca de lo que estoy tratando de lograr, pero no quiero usar handle
que oculta la excepción. Gracias por cualquier idea.
No creo CompletionStage
ni CompletableFuture
proporciona ningún método sencillo para esto. Sin embargo, la combinación handle
con thenCompose
debe hacer lo que quiera, si entiendo correctamente a sus necesidades.
Una handle
etapa se ejecuta si la etapa principal ha completado normalmente o, excepcionalmente, y le da acceso al resultado o error, respectivamente. A partir de esta etapa se podía volver otra CompletionStage
que, o bien se ha completado con normalidad o, excepcionalmente, en función de qué argumentos la handle
etapa recibe.
handle((T result, Throwable error) -> {
if (error != null) {
return CompletableFuture.<T>failedStage(error);
} else {
return processResult(result); // returns CompletionStage<T>
}
});
Ahora usted tiene una CompletionStage<CompletionStage<T>>
. Ahora llevamos a cabo una operación de mapa plano mediante la invocación thenCompose
:
thenCompose(Function.identity());
Lo que nos da una CompletionStage<T>
. Esto CompletionStage<T>
será lo que sea ejemplo fue devuelto por handle
. Si esa instancia fue una etapa fallidas y se sigue la excepción se propaga; de lo contrario, el resultado se pasa a cualquier etapa depende de la thenCompose
etapa y el proceso continúa normalmente.
Esto se puede ver con el siguiente ejemplo:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class Main {
public static void main(String[] args) {
methodThatReturnsCompletionStage()
.handle((result, error) -> {
if (error != null) {
return CompletableFuture.<String>failedStage(error);
} else {
return processResult(result);
}
})
.thenCompose(future -> {
System.out.println("#thenCompose invoked");
return future; // Identity function
})
.thenApply(result -> {
System.out.println("#thenApply invoked");
return result; // Identity function (exists to show intermediary stage)
})
.whenComplete((result, error) -> {
System.out.println("#whenComplete invoked");
if (error != null) {
error.printStackTrace(System.out);
} else {
System.out.println(result);
};
});
}
private static CompletionStage<String> methodThatReturnsCompletionStage() {
return CompletableFuture.completedStage("Hello");
// return CompletableFuture.failedStage(new RuntimeException("OOPS"));
}
private static CompletionStage<String> processResult(String result) {
return CompletableFuture.completedFuture(result + ", World!");
}
}
Esto resultará en cada etapa que se invoca y una salida de Hello, World!
. Pero si se cambia methodThatReturnsCompletionStage()
para volver al escenario fallado entonces thenApply
se salta (porque el futuro ha fallado) y se le da a la excepción whenComplete
(que, al igual que handle
, se invoca tanto para la finalización normal o excepcional).
Nota: Todo lo anterior se utiliza la CompletionStage
interfaz directamente, sino mediante CompletableFuture
funciona igual de bien (y puede ser preferible).