4 Obtención del valor con el que se ha completado¶
Bloqueante y con checked exceptions¶
En el ejemplo anterior hemos usado el método get()
para obtener el valor con el que se ha completado el CompletableFuture. Este método hace que el hilo actual espere a que el CompletableFuture sea completado, retornando finalmente su valor. Lanza la excepción CancellationException
, si el CompletableFuture fue cancelado, ExecutionException
si el CompletableFuture fue completado con una excepción, o InterruptedException
, si el hilo actual fue interrumpido mientras estaba esperando a que el CompletableFuture fuera completado. Debemos tener en cuenta que estas dos últimas excepciones son checked exceptions, por lo que estamos obligados a capturarlas.
El método get(timeout, timeoutUnit)
está sobrecargado para que podamos establecer un tiempo máximo de espera, especificado en una determinada unidad de tiempo. Si transcurrido dicho tiempo máximo el CompletableFuture no ha sido completo, se lanzará la excepción TimeoutException
.
Bloqueante sin checked exceptions¶
Los métodos anteriores ya estaban presentes en la interfaz Future
. Sin embargo, la clase CompletableFuture
incorpora dos métodos parecidos a get()
, pero con matices distintos. El primero de ellos es el método join()
, que tiene la misma funcionalidad que get()
pero a diferencia de éste no lanza ninguna checked excepción permitiéndonos una sintaxis más legible. Lanza la excepción CancellationException
si el CompletableFuture fue cancelado o CompletionException
, si el CompletableFuture se completó con una excepción o la tarea lanzó una excepción.
Así el ejemplo anterior usando join()
quedaría como:
private void joinExample() {
CompletableFuture<Integer> cf = CompletableFuture.supplyAsync(this::generateNumber);
Integer value = cf.join();
printNumber(value);
}
private int generateNumber() {
System.out.printf("%s - Supplier\n", Thread.currentThread().getName());
return 2;
}
private void printNumber(Integer value) {
System.out.printf("%s - %d\n",
Thread.currentThread().getName(), value);
}
No bloqueante con valor por defecto¶
Pero además tenemos disponible una nueva posibilidad, correspondiente al método getNow(default)
, que obtiene el valor con el que se ha completado el CompletableFuture y si éste no está aún no se ha completado retorna el valor recibido como parámetro como default. Por tanto, se trata de un método NO bloqueante, ya siempre retornará un valor inmediatamente, ya sea porque ya se haya obtenido desde la tarea asíncrona o porque se retorne el valor por defecto. Lanza la excepción CancellationException
si el CompletableFuture fue cancelado o CompletionException
, si el CompletableFuture se completó con una excepción o la tarea lanzó una excepción.
Modifiquemos el ejemplo anterior:
private void getNowExample() {
CompletableFuture<Integer> cf = CompletableFuture.supplyAsync(this::generateNumber);
Integer value = cf.getNow(0);
printNumber(value);
sleep(3000);
Integer value2 = cf.getNow(0);
printNumber(value2);
}
private int generateNumber() {
sleep(2000);
System.out.printf("%s - Supplier\n", Thread.currentThread().getName());
return 2;
}
private void printNumber(Integer value) {
System.out.printf("%s - Main - %d\n",
Thread.currentThread().getName(), value);
}
private boolean sleep(long timeInMilis) {
try {
Thread.sleep(timeInMilis);
return true;
} catch (InterruptedException e) {
return false;
}
}