CompletableFuture no funciona si se guarda en una variable.
¿Por qué no funciona cuando guardo el futuro en una variable antes de los pasos de configuración?
@Test
void simple() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(this::throwException)
.exceptionally(throwable -> HANDLED);
assertEquals(HANDLED, future.join());
}
@Test
void withVar() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(this::throwException);
future.exceptionally(throwable -> HANDLED);
assertEquals(HANDLED, future.join());
}
private String throwException() {
if (true) {
throw new RuntimeException(FAIL);
}
return SUCCESS;
}
simple()
está bien, pero withVar()
no funciona:
java.util.concurrent.CompletionException: java.lang.RuntimeException: FAIL
en java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314)
en java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319)
en java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1702)
en java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
en java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
en java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
en java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
en java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
en java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)`
Causado por: java.lang.RuntimeException: FAIL
en ru.dionisis.resttest.ComplitableFutureTest.throwException(ComplitableFutureTest.java:35)
en java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
... 6 más`
Hubo un problema similar con RestTemplateBuilder
, cuando quería hacer opcional la inicialización de un campo
davy.ai
El problema con
withVar()
es queexceptionally
devuelve un nuevoCompletableFuture
con el valor de error manejado, pero no modifica elCompletableFuture
original que se guardó en la variablefuture
. Por lo tanto, cuando se llama ajoin()
enfuture
, todavía contiene la excepción no manejada y provoca que la prueba falle.En
simple()
, el métodoexceptionally
se llama directamente en elCompletableFuture
, por lo que el manejo se aplica al mismo futuro que se une posteriormente.Para solucionar el problema con
withVar()
, puede guardar el nuevoCompletableFuture
devuelto porexceptionally
en la variablefuture
, de esta manera:De esta manera, la variable
future
apunta al nuevo objetoCompletableFuture
que contiene el valor de error manejado yjoin()
devuelve el resultado esperado.