Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprende CompletableFuture | Mejores Prácticas de Multithreading
Multithreading en Java

bookCompletableFuture

¡Queda un último paso! En este capítulo analizaremos la principal clase de asincronía CompletableFuture.

Utilicemos una analogía de la vida real. Solicitas un taxi mediante una aplicación (creación de tarea). Mientras esperas, la aplicación puede proporcionar actualizaciones sobre la ubicación del vehículo o el tiempo estimado de llegada (procesamiento de resultados). Si surgen problemas como retrasos o cancelaciones, la aplicación te notificará y sugerirá alternativas (gestión de excepciones).

Métodos principales

La primera pregunta que podrías hacerte es cómo iniciar una tarea utilizando CompletableFuture. Para ello, puedes utilizar el método supplyAsync(), que está diseñado para ejecutar una tarea que devuelve el resultado de forma asíncrona.

Main.java

Main.java

copy
12345678910
// Creating an asynchronous task CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { return "Result"; // Result }); // Processing the result after the task completes future.thenAccept(result -> { // Print the result to the console System.out.println("Result received: " + result); // Result received: });

También contamos con el método thenAccept(), que maneja el valor devuelto por CompletableFuture en código asíncrono. No retorna ningún valor; resulta útil cuando se necesita aceptar una respuesta y procesarla de alguna manera.

Note
Nota

En nuestro ejemplo, ejecutamos la tarea de forma asíncrona, y future.thenAccept() obtiene la respuesta de la lambda, que luego se puede utilizar para imprimir en la consola.

Existe un método similar, thenApply(), que funciona como thenAccept() pero obtiene el resultado de la tarea asíncrona y devuelve un nuevo resultado.

Main.java

Main.java

copy
12345678910
// Creating an asynchronous task that returns "Hello, World!" CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello, World!"); // Transforming the result to uppercase CompletableFuture<String> transformedFuture = future.thenApply(result -> result.toUpperCase()); // Printing the transformed result to the console transformedFuture.thenAccept(result -> { System.out.println("Transformed result: " + result); // Transformed result: });

¿Qué sucede si no se desea obtener el resultado de la tarea asíncrona, sino simplemente ser notificado cuando haya finalizado?

Para esto, se puede utilizar thenRun(), que se ejecuta después de que la tarea asíncrona finaliza.

Main.java

Main.java

copy
12345678
// Creating an asynchronous task that returns "Hello, World!" CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello, World!"); // Running a task when the previous task completes future.thenRun(() -> { // Print message to the console indicating that the task is complete System.out.println("Task completed!"); // Task completed! });

También es posible obtener el resultado de una tarea asíncrona bloqueando el hilo actual hasta que la tarea esté completada. Para este propósito, se utiliza el método get().

Main.java

Main.java

copy
1234567
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello World"); try { String result = completableFuture.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }

También existe un método join(), que igualmente pausa el hilo actual y espera a que la tarea asíncrona finalice. Sin embargo, la diferencia entre join() y get() es que lanzan excepciones diferentes.

Main.java

Main.java

copy
12345
// Create a `CompletableFuture` that asynchronously executes a task and returns the string "Hello World". CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello World"); // The `join()` method blocks the current thread until the task is completed and returns the result "Hello World". String result = completableFuture.join();

Aquí no gestionamos la excepción porque el método join() lanza una CompletionException no comprobada.

Combinación y Encadenamiento de Tareas

Es posible combinar tareas en CompletableFuture utilizando thenCompose(), que permite unir dos tareas dependientes.

Main.java

Main.java

copy
12345678910111213141516171819202122232425
// Method to get book details from a remote service public CompletableFuture<String> getBookDetails() { return CompletableFuture.supplyAsync(() -> { // Request to a remote service to get book details return "Book Details"; // Placeholder for book details }); } // Method to get author details from a remote service public CompletableFuture<String> getAuthorDetails(String bookDetails) { return CompletableFuture.supplyAsync(() -> { // Request to another service to get author details return bookDetails + " Author"; // Placeholder for author details }); } // Combine two asynchronous tasks: get book details and then get author details CompletableFuture<String> result = getBookDetails() .thenCompose(book -> getAuthorDetails(book)); // Process the result and print the author details to the console result.thenAccept(author -> { // Print the author details to the console System.out.println("Author: " + author); // Author details });

Primero se recuperan los datos del libro de forma asíncrona utilizando el método getBookDetails(), y luego se usa el resultado para realizar la siguiente tarea asíncrona: obtener los datos del autor mediante el método getAuthorDetails(). Una vez completadas ambas tareas, el resultado (información del autor) se muestra en la consola.

También es posible fusionar los resultados de dos tareas utilizando el método thenCombine(). Ejecuta ambas tareas concurrentemente y combina sus resultados una vez que ambas tareas han finalizado.

Main.java

Main.java

copy
123456789101112
// CompletableFuture for adding two numbers CompletableFuture<Double> firstNumberFuture = CompletableFuture.supplyAsync(() -> 50.0); CompletableFuture<Double> secondNumberFuture = CompletableFuture.supplyAsync(() -> 30.0); // Combine the two futures by adding their results CompletableFuture<Double> sumFuture = firstNumberFuture.thenCombine(secondNumberFuture, (first, second) -> first + second); // Print the result of the addition to the console sumFuture.thenAccept(sum -> { System.out.println("Sum: " + sum); // Sum: 80.0 });

Este código recupera de forma asíncrona dos números, los suma y muestra el resultado en la consola.

También es posible esperar a que todas las tareas finalicen utilizando el método allOf(), o a que cualquiera de ellas finalice utilizando el método anyOf().

Main.java

Main.java

copy
1234567891011121314151617181920
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> { // Task 1 System.out.println("Task 1 completed"); }); CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> { // Task 2 System.out.println("Task 2 completed"); }); // Combine both futures and wait for both tasks to complete CompletableFuture<Void> combinedFuture1 = CompletableFuture.allOf(future1, future2); // Combine both futures and proceed as soon as any one task completes CompletableFuture<Object> combinedFuture2 = CompletableFuture.anyOf(future1, future2); // Print a message after all tasks are completed combinedFuture1.thenRun(() -> { System.out.println("All tasks completed"); });

¿Cómo se manejan los errores y qué es un tiempo de espera?

Clip corto del video

  • handle(): Procesa el resultado o gestiona cualquier excepción lanzada por el CompletableFuture;
  • exceptionally(): Gestiona excepciones lanzadas durante la ejecución del CompletableFuture;
  • completeOnTimeout(): Completa el CompletableFuture con un valor especificado si se agota el tiempo antes de completarse.
Note
Nota

CompletableFuture facilita la gestión de tareas asíncronas y el procesamiento de sus resultados, lo que lo convierte en una herramienta potente para el desarrollo de aplicaciones modernas.

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 4. Capítulo 6

Pregunte a AI

expand

Pregunte a AI

ChatGPT

Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla

Suggested prompts:

What are some practical examples of using CompletableFuture in real applications?

Can you explain the difference between thenAccept(), thenApply(), and thenRun()?

How do you handle exceptions when working with CompletableFuture?

Awesome!

Completion rate improved to 3.33

bookCompletableFuture

Desliza para mostrar el menú

¡Queda un último paso! En este capítulo analizaremos la principal clase de asincronía CompletableFuture.

Utilicemos una analogía de la vida real. Solicitas un taxi mediante una aplicación (creación de tarea). Mientras esperas, la aplicación puede proporcionar actualizaciones sobre la ubicación del vehículo o el tiempo estimado de llegada (procesamiento de resultados). Si surgen problemas como retrasos o cancelaciones, la aplicación te notificará y sugerirá alternativas (gestión de excepciones).

Métodos principales

La primera pregunta que podrías hacerte es cómo iniciar una tarea utilizando CompletableFuture. Para ello, puedes utilizar el método supplyAsync(), que está diseñado para ejecutar una tarea que devuelve el resultado de forma asíncrona.

Main.java

Main.java

copy
12345678910
// Creating an asynchronous task CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { return "Result"; // Result }); // Processing the result after the task completes future.thenAccept(result -> { // Print the result to the console System.out.println("Result received: " + result); // Result received: });

También contamos con el método thenAccept(), que maneja el valor devuelto por CompletableFuture en código asíncrono. No retorna ningún valor; resulta útil cuando se necesita aceptar una respuesta y procesarla de alguna manera.

Note
Nota

En nuestro ejemplo, ejecutamos la tarea de forma asíncrona, y future.thenAccept() obtiene la respuesta de la lambda, que luego se puede utilizar para imprimir en la consola.

Existe un método similar, thenApply(), que funciona como thenAccept() pero obtiene el resultado de la tarea asíncrona y devuelve un nuevo resultado.

Main.java

Main.java

copy
12345678910
// Creating an asynchronous task that returns "Hello, World!" CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello, World!"); // Transforming the result to uppercase CompletableFuture<String> transformedFuture = future.thenApply(result -> result.toUpperCase()); // Printing the transformed result to the console transformedFuture.thenAccept(result -> { System.out.println("Transformed result: " + result); // Transformed result: });

¿Qué sucede si no se desea obtener el resultado de la tarea asíncrona, sino simplemente ser notificado cuando haya finalizado?

Para esto, se puede utilizar thenRun(), que se ejecuta después de que la tarea asíncrona finaliza.

Main.java

Main.java

copy
12345678
// Creating an asynchronous task that returns "Hello, World!" CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello, World!"); // Running a task when the previous task completes future.thenRun(() -> { // Print message to the console indicating that the task is complete System.out.println("Task completed!"); // Task completed! });

También es posible obtener el resultado de una tarea asíncrona bloqueando el hilo actual hasta que la tarea esté completada. Para este propósito, se utiliza el método get().

Main.java

Main.java

copy
1234567
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello World"); try { String result = completableFuture.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }

También existe un método join(), que igualmente pausa el hilo actual y espera a que la tarea asíncrona finalice. Sin embargo, la diferencia entre join() y get() es que lanzan excepciones diferentes.

Main.java

Main.java

copy
12345
// Create a `CompletableFuture` that asynchronously executes a task and returns the string "Hello World". CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello World"); // The `join()` method blocks the current thread until the task is completed and returns the result "Hello World". String result = completableFuture.join();

Aquí no gestionamos la excepción porque el método join() lanza una CompletionException no comprobada.

Combinación y Encadenamiento de Tareas

Es posible combinar tareas en CompletableFuture utilizando thenCompose(), que permite unir dos tareas dependientes.

Main.java

Main.java

copy
12345678910111213141516171819202122232425
// Method to get book details from a remote service public CompletableFuture<String> getBookDetails() { return CompletableFuture.supplyAsync(() -> { // Request to a remote service to get book details return "Book Details"; // Placeholder for book details }); } // Method to get author details from a remote service public CompletableFuture<String> getAuthorDetails(String bookDetails) { return CompletableFuture.supplyAsync(() -> { // Request to another service to get author details return bookDetails + " Author"; // Placeholder for author details }); } // Combine two asynchronous tasks: get book details and then get author details CompletableFuture<String> result = getBookDetails() .thenCompose(book -> getAuthorDetails(book)); // Process the result and print the author details to the console result.thenAccept(author -> { // Print the author details to the console System.out.println("Author: " + author); // Author details });

Primero se recuperan los datos del libro de forma asíncrona utilizando el método getBookDetails(), y luego se usa el resultado para realizar la siguiente tarea asíncrona: obtener los datos del autor mediante el método getAuthorDetails(). Una vez completadas ambas tareas, el resultado (información del autor) se muestra en la consola.

También es posible fusionar los resultados de dos tareas utilizando el método thenCombine(). Ejecuta ambas tareas concurrentemente y combina sus resultados una vez que ambas tareas han finalizado.

Main.java

Main.java

copy
123456789101112
// CompletableFuture for adding two numbers CompletableFuture<Double> firstNumberFuture = CompletableFuture.supplyAsync(() -> 50.0); CompletableFuture<Double> secondNumberFuture = CompletableFuture.supplyAsync(() -> 30.0); // Combine the two futures by adding their results CompletableFuture<Double> sumFuture = firstNumberFuture.thenCombine(secondNumberFuture, (first, second) -> first + second); // Print the result of the addition to the console sumFuture.thenAccept(sum -> { System.out.println("Sum: " + sum); // Sum: 80.0 });

Este código recupera de forma asíncrona dos números, los suma y muestra el resultado en la consola.

También es posible esperar a que todas las tareas finalicen utilizando el método allOf(), o a que cualquiera de ellas finalice utilizando el método anyOf().

Main.java

Main.java

copy
1234567891011121314151617181920
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> { // Task 1 System.out.println("Task 1 completed"); }); CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> { // Task 2 System.out.println("Task 2 completed"); }); // Combine both futures and wait for both tasks to complete CompletableFuture<Void> combinedFuture1 = CompletableFuture.allOf(future1, future2); // Combine both futures and proceed as soon as any one task completes CompletableFuture<Object> combinedFuture2 = CompletableFuture.anyOf(future1, future2); // Print a message after all tasks are completed combinedFuture1.thenRun(() -> { System.out.println("All tasks completed"); });

¿Cómo se manejan los errores y qué es un tiempo de espera?

Clip corto del video

  • handle(): Procesa el resultado o gestiona cualquier excepción lanzada por el CompletableFuture;
  • exceptionally(): Gestiona excepciones lanzadas durante la ejecución del CompletableFuture;
  • completeOnTimeout(): Completa el CompletableFuture con un valor especificado si se agota el tiempo antes de completarse.
Note
Nota

CompletableFuture facilita la gestión de tareas asíncronas y el procesamiento de sus resultados, lo que lo convierte en una herramienta potente para el desarrollo de aplicaciones modernas.

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 4. Capítulo 6
some-alt