Saltar al contenido

Lanzamiento de excepción de CompletableFuture

Solución:

Su código sugiere que está utilizando el resultado de la operación asincrónica más adelante en el mismo método, por lo que tendrá que lidiar con CompletionException de todos modos, una forma de lidiar con eso es

public void myFunc() throws ServerException {
    // Some code
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
        try { return someObj.someFunc(); }
        catch(ServerException ex) { throw new CompletionException(ex); }
    });
    // Some code running in parallel to someFunc()

    A resultOfA;
    try {
        resultOfA = a.join();
    }
    catch(CompletionException ex) {
        try {
            throw ex.getCause();
        }
        catch(Error|RuntimeException|ServerException possible) {
            throw possible;
        }
        catch(Throwable impossible) {
            throw new AssertionError(impossible);
        }
    }
    // some code using resultOfA
}

Todas las excepciones lanzadas dentro del procesamiento asincrónico del Supplier se envolverá en un CompletionException al llamar join, excepto el ServerException ya hemos envuelto en un CompletionException.

Cuando volvemos a lanzar la causa de la CompletionException, podemos enfrentar excepciones no controladas, es decir, subclases de Error o RuntimeException, o nuestra excepción de verificación personalizada ServerException. El código anterior los maneja a todos con una captura múltiple que los volverá a lanzar. Dado que el tipo de retorno declarado de getCause() es Throwable, el compilador requiere que manejemos ese tipo a pesar de que ya manejamos todos los tipos posibles. La solución sencilla es lanzar este imposible arrojadizo envuelto en un AssertionError.

Alternativamente, podríamos usar un resultado futuro alternativo para nuestra excepción personalizada:

public void myFunc() throws ServerException {
    // Some code
    CompletableFuture<ServerException> exception = new CompletableFuture<>();
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
        try { return someObj.someFunc(); }
        catch(ServerException ex) {
            exception.complete(ex);
            throw new CompletionException(ex);
        }
    });
    // Some code running in parallel to someFunc()

    A resultOfA;
    try {
        resultOfA = a.join();
    }
    catch(CompletionException ex) {
        if(exception.isDone()) throw exception.join();
        throw ex;
    }

    // some code using resultOfA
}

Esta solución volverá a lanzar todos los arrojables “inesperados” en su forma envuelta, pero solo arrojará los personalizados ServerException en su forma original pasó a través del exception futuro. Tenga en cuenta que tenemos que asegurarnos de que a se ha completado (como llamar join() primero), antes de consultar el exception futuro, para evitar condiciones de carrera.

Para aquellos que buscan otras formas de manejo de excepciones con completableFuture

A continuación, se muestran varias formas, por ejemplo, de manejar el error de análisis a entero:

1. Utilizando handle método – que le permite proporcionar un valor predeterminado en caso de excepción

CompletableFuture correctHandler = CompletableFuture.supplyAsync(() -> "A")
            .thenApply(Integer::parseInt)
            .handle((result, ex) -> {
                if (null != ex) {
                    ex.printStackTrace();
                    return 0;
                } else {
                    System.out.println("HANDLING " + result);
                    return result;
                }
            })
            .thenAcceptAsync(s -> {
                System.out.println("CORRECT: " + s);
            });

2. Usando exceptionally Método – Similar a handle pero menos detallado

CompletableFuture parser = CompletableFuture.supplyAsync(() -> "1")
                .thenApply(Integer::parseInt)
                .exceptionally(t -> {
                    t.printStackTrace();
                    return 0;
                }).thenAcceptAsync(s -> System.out.println("CORRECT value: " + s));

3. Usando whenComplete Método – el uso de esto detendrá el método en sus pistas y no ejecutará el siguiente thenAcceptAsync

CompletableFuture correctHandler2 = CompletableFuture.supplyAsync(() -> "A")
                .thenApply(Integer::parseInt)
                .whenComplete((result, ex) -> {
                    if (null != ex) {
                        ex.printStackTrace();
                    }
                })
                .thenAcceptAsync(s -> {
                    System.out.println("When Complete: " + s);
                });

4. Propagar la excepción a través de completeExceptionally

public static CompletableFuture<Integer> converter(String convertMe) {
        CompletableFuture<Integer> future = new CompletableFuture<>();
        try {
            future.complete(Integer.parseInt(convertMe));
        } catch (Exception ex) {
            future.completeExceptionally(ex);
        }
        return future;
    }

Creo que deberías envolver eso en un RuntimeException y tira eso:

 throw new RuntimeException(ex);

O muchos serían una pequeña utilidad que ayudaría:

static class Wrapper extends RuntimeException {

    private Wrapper(Throwable throwable) {
        super(throwable);
    }

    public static Wrapper wrap(Throwable throwable) {
        return new Wrapper(throwable);
    }

    public Throwable unwrap() {
        return getCause();
    }
}


 public static void go() {
    CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> {
        try {
            throw new Exception("Just because");
        } catch (Exception ex) {
            throw Wrapper.wrap(ex);
        }
    });

    a.join();
}

Y luego podrías unwrap ese..

 try {
        go();
 } catch (Wrapper w) {
        throw w.unwrap();
 }
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *