Vuelvo después de la respuesta al usuario.
class FinalResponseDTO { List<Service1ResponseDTO> service1ResponseDTO; Long totalCount; List<Service2ResponseDTO> service2ResponseDTO; } A partir de ahora estoy haciendo tres llamadas secuenciales para calcular este FinalResponseDTO , cada una de estas llamadas se puede ejecutar independientemente de las demás. Intenté hacer tres CompletableFuture diferentes como:
CompletableFuture<List<Service1ResponseDTO> future1 = CompletableFuture.supplyAsync(() -> service1.callMethod1()); CompletableFuture<Long> future2 = CompletableFuture.supplyAsync(() -> service2.callMethod2()); CompletableFuture<Service2ResponseDTO> future3 = CompletableFuture.supplyAsync(() -> service3.callMethod3()); Si hago CompletableFuture.allOf(future1, future2, future3).join(); o ¿Debería llamar a CompletableFuture.allOf(future1, future2, future3).get(); ? Incluso si llamo a cualquiera de estos join u get , ¿cómo debo construir FinalResponseDTO a partir de él? Soy nuevo en las funciones de concurrencia de Java 8 como CompletableFuture y estoy confundido, ya que cada tipo de retorno de cada uno de estos futuros es diferente, ¿cómo debo obtener respuestas combinadas de todos esos futuros y luego construir mi salida final?
De los Javadocs de CompletableFuture.allOf() :
Devuelve un nuevo CompletableFuture que se completa cuando se completan todos los CompletableFutures proporcionados. Si cualquiera de los CompletableFutures proporcionados se completa excepcionalmente, entonces el CompletableFuture devuelto también lo hace, con una CompletionException que contiene esta excepción como su causa. De lo contrario, los resultados, si los hay, de los CompletableFutures proporcionados no se reflejan en el CompletableFuture devuelto, pero se pueden obtener inspeccionándolos individualmente .
Entonces, cuando se completa la combinación CompletableFuture, puede inspeccionar los valores y construir el objeto de respuesta final usando un constructor simple aplicando una función que construye el objeto:
CompletableFuture.allOf(future1, future2, future3).thenApply(v -> new FinalResponseDTO(future1.join(), future2.join(), future3.join()) ); CompletableFuture<List<Service1ResponseDTO> future1 = CompletableFuture.supplyAsync(() -> service1.callMethod1()); CompletableFuture<Long> future2 = CompletableFuture.supplyAsync(() -> service2.callMethod2()); CompletableFuture<List<Service2ResponseDTO>> future3 = CompletableFuture.supplyAsync(() -> service3.callMethod3()); CompletableFuture.allOf(future1, future2, future3).get(); return new FinalResponseDTO(future1.join(), future2.join(), future3.join()); Nota: supplyAsync se ejecuta en ForkJoinPool.commonPool() , por lo que sería una buena opción proporcionar su propio ejecutor , por ejemplo Executors.newCachedThreadPool() :
CompletableFuture.supplyAsync(() -> action, executor);En lugar de usar CompletableFuture.allOf , podría recurrir al Applicative algo oculto de CompletableFuture :
static <T, R> CompletableFuture<R> alsoApply(CompletableFuture<T> future, CompletableFuture<Function<T, R>> f) { return f.thenCompose(future::thenApply); }Con esta función de ayuda puede ejecutar futuros en subprocesos paralelos:
CompletableFuture<String> future = alsoApply( CompletableFuture.supplyAsync(() -> "a"), CompletableFuture.supplyAsync(() -> "b") .thenApply(b -> a -> a + b)); assertEquals("ab", future.get());Vea esta pregunta y respuesta para saber de dónde viene esto y por qué y cómo funciona.