java-8 - completablefuture - java 8 promise
CompletableFuture, supplyAsync() y luegoApply() (2)
Necesito confirmar algo. El siguiente código:
CompletableFuture
.supplyAsync(() -> {return doSomethingAndReturnA();})
.thenApply(a -> convertToB(a));
sería lo mismo que:
CompletableFuture
.supplyAsync(() -> {
A a = doSomethingAndReturnA();
convertToB(a);
});
¿Derecha?
Además, otras dos preguntas que siguen como "¿hay alguna razón por la que thenApply
?"
1) ¿Tener código grande para la conversión?
o
2) ¿Necesitas reutilizar el bloque lambda en otros lugares?
No es lo mismo . En el segundo ejemplo en el que thenApply
no se usa, es cierto que la llamada a convertToB
se ejecuta en el mismo hilo que el método doSomethingAndReturnA
.
Pero, en el primer ejemplo, cuando se thenApply
método thenApply
, pueden suceder otras cosas.
En primer lugar, si se ha completado CompletableFuture
que ejecuta doSomethingAndReturnA
, la invocación de thenApply
se realizará en el subproceso de la persona que llama. Si no se ha completado CompletableFutures, la Function
pasada a thenApply
se invocará en el mismo hilo que doSomethingAndReturnA
.
¿Confuso? Bueno, este artículo podría ser útil (gracias @SotiriosDelimanolis por el enlace).
He proporcionado un breve ejemplo que ilustra cómo funciona thenApply
.
public class CompletableTest {
public static void main(String... args) throws ExecutionException, InterruptedException {
final CompletableFuture<Integer> future = CompletableFuture
.supplyAsync(() -> doSomethingAndReturnA())
.thenApply(a -> convertToB(a));
future.get();
}
private static int convertToB(final String a) {
System.out.println("convertToB: " + Thread.currentThread().getName());
return Integer.parseInt(a);
}
private static String doSomethingAndReturnA() {
System.out.println("doSomethingAndReturnA: " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "1";
}
}
Y la salida es:
doSomethingAndReturnA: ForkJoinPool.commonPool-worker-1
convertToB: ForkJoinPool.commonPool-worker-1
Entonces, cuando la primera operación es lenta (es decir, el CompletableFuture
aún no se ha completado), ambas llamadas se producen en el mismo hilo. Pero si elimináramos el Thread.sleep
de doSomethingAndReturnA
la salida (puede) ser así:
doSomethingAndReturnA: ForkJoinPool.commonPool-worker-1
convertToB: main
Tenga en cuenta que la llamada convertToB
está en el hilo main
.
thenApply()
es una función de devolución de llamada, que se ejecutará cuando supplyAsync()
devuelva un valor.
En el fragmento de código 2, el subproceso que invocó a doSomethingAndReturnA()
espera a que la función se ejecute y devuelva los datos.
Pero en algunos casos excepcionales (como hacer una llamada al servicio web y esperar una respuesta), el subproceso tiene que esperar mucho tiempo para obtener la respuesta, lo que consume una gran cantidad de recursos de cómputo del sistema (solo espera la respuesta).
Para evitar eso, CompletableFuture
viene con la función de devolución de llamada , donde una vez que se invoca doSomethingAndReturnA()
, un subproceso independiente se encargará de ejecutar doSomethingAndReturnA()
y el subproceso principal del llamante continuará realizando otras operaciones sin esperar la respuesta.
Una vez que esté disponible la respuesta de doSomethingAndReturnA
, se invocará el método de devolución de llamada (es decir, a thenApply()
, thenApply()
)