es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿CompletableFuture no cuenta con un método para asignar valores incluso en caso de errores?

Tengo un programa que realiza solicitudes HTTP, donde podría necesitar probar varios servidores diferentes para obtener una respuesta exitosa. El cliente HTTP es asíncrono, por lo que obtengo resultados de CompletableFuture al realizar las solicitudes.

Puedo intentar fácilmente el “siguiente” servidor cuando obtengo un código de estado de respuesta incorrecto, por ejemplo:

return httpClient.sendAsync(request,
                HttpResponse.BodyHandlers.ofByteArray()
).thenCompose(response -> {
    if(response.statusCode() == 200){
        return completedStage(...); //todo bien, ¡terminado!
    }
    // intentar el siguiente servidor
    return callThisMethodAgain(...);
});

Esto funciona porque thenCompose espera que el lambda que se le haya proporcionado devuelva otro CompletableFuture para que pueda encadenar cálculos asíncronos.

Sin embargo, si el servidor no está disponible, el cliente HTTP lanzará una excepción y mi lambda no se ejecutará, lo que hará que la cadena asíncrona termine con esa excepción.

Puedo ver que hay métodos como handle y handleAsync, pero no me permiten devolver otro CompletableFuture, como lo hace thenCompose. Son algo análogos a thenApply (es decir, como el map del stream en lugar de flatMap), por lo que parece que CompletableFuture carece del método que sería análogo a flatMap donde quiero manejar tanto el éxito como los fallos de la acción asíncrona anterior en la cadena.

¿Es eso correcto? ¿Hay alguna solución alternativa que esté pasando por alto?

Tags:  ,

Answer

  1. Avatar for davy.ai

    Sí, tienes razón en que CompletableFuture no tiene un método directo análogo a flatMap en los streams, que pueda manejar tanto casos de éxito como de fallo y devolver otro CompletableFuture para encadenar. Sin embargo, puedes lograr una funcionalidad similar utilizando el método handle y CompletableFuture.completedFuture para devolver otro CompletableFuture para encadenar.

    Aquí tienes un ejemplo:

    return httpClient.sendAsync(request,
                    HttpResponse.BodyHandlers.ofByteArray()
    ).handle((response, throwable) -> {
        if (response != null && response.statusCode() == 200) {
            return completedStage(...); // ¡todo bien, listo!
        } else if (throwable != null) {
            return completedFuture(...); // manejar la excepción
        } else {
            // intentar con el siguiente servidor
            return callThisMethodAgain(...);
        }
    }).thenCompose(Function.identity());
    

    En este ejemplo, handle toma una BiFunction que maneja tanto la HttpResponse como la Throwable devueltas por sendAsync. Si la respuesta no es nula y tiene un código de estado 200, devuelve un CompletableFuture completado. Si se lanzó una excepción, devuelve un CompletableFuture completado que maneja la excepción. De lo contrario, intenta con el siguiente servidor devolviendo una llamada a callThisMethodAgain.

    Ten en cuenta que el thenCompose al final es necesario para aplanar el CompletableFuture devuelto por handle y permitir una mayor encadenamiento.

    ¡Espero que eso ayude! Déjame saber si tienes alguna otra pregunta.

Comments are closed.