Hi again,
To be a bit more clear, here is what I wanted to say and I will detail it in a more specific use case :
When using cascading async calls, one of the handlers can receive or raise an exception that you want to handle in the current scope,
however sometimes there is also the need to tell the scopes above; when either you want to try something else on failure or that it does not feel natural that the scopes underneath handle the error.
For example, you don't want to pass objects that hold the full context to handlers so they get in charge of telling a potential client that something went wrong, it simply does feel right for me to fit it there.
We found something interesting that might be what we need, i.e. CompletableFuture, here is a practical example of what we are doing :
void postModel(RoutingContext routingContext) {
HttpServerResponse response = routingContext.response();
// create a model object
Model model = new Model(routingContext.request().headers());
if (model.isValid()) {
// set modelid in redis and add a new task to rabbitmq
redisClient.storeModel(model)
.thenCompose(fn -> rabbitMQClient.publishModel(model))
.handle((ok, exception) -> {
if (exception == null) {
response.setStatusCode(200).end();
} else {
logger.error(exception.getMessage(), exception);
response.setStatusCode(500).end(exception.getMessage());
}
return 1;
});
} else {
response.setStatusCode(400).end("Bad request. Invalid Model");
}
}
public CompletableFuture<Void> storeModel(Model model) {
// creates the future
CompletableFuture<Void> future = new CompletableFuture<>();
// store in redis
redisClient.setnx(model.getName(), model.getKey(), asyncResult -> {
if (asyncResult.succeeded()){
// successfully stored in redis, we complete the future
future.complete(null);
} else if (asyncResult.failed()) {
// failure. We complete the future and report the exception
future.completeExceptionally(asyncResult.cause());
}
});
// return the futures
return future;
}
Here we wrap the async calls within a method to benefit from the completableFuture features to handle an exception on the outer scope. It seems more natural since the a vertx handler is not made for chaining calls since we can't throw exceptions in them.