The Future Is Here

Getting asynchronous operations right is hard. And when you have to pass a result of one function to the next, it can only get worse. Since today, vertx-jooq’s API for instance only allowed asynchronous operations with a callback handler, which leads to code fragments like this:


//fetch something from A to make a lookup on B
ADao adao = new ADao();
adao.findByIdAsync(123,h1->{
if(h1.succeeded()){
BDao bdao = new BDao();
bdao.findBySomeAValueAsync(h1.result().getSomeAValue(),h2->{
if(h2.succeeded()){
//do something with the result
}else{
//oops something went wrong
}
});
}else{
//oops something went wrong
}
});

What annoys me about this is that I have to define in each handler if it succeeded and what to do if an exception occurred on the database layer. Especially when you have to nest three or more operations this looks ugly. Of course this problem is not new and there exists an alternative approach which is using and composing Java 8 java.util.concurrent.CompletableFuture. By doing so, the same code becomes easier and more readable:


//fetch something from A to make a lookup on B
ADao adao = new ADao();
CompletableFuture<A> aFutureLoaded = adao.findByIdAsync(123);
BDao bdao = new BDao();
aFutureLoaded.thenCompose(
a->bdao.findBySomeAValueAsync(a.getSomeAValue());
).whenComplete((bResult,ex)->{
if(ex==null){
//do something with the result
}else{
//oops something went wrong
}
});

But using CompletableFuture within the Vertx world leads to a problem: Vertx has it’s own threading model to achieve the performance it actually has. On the other hand, some methods of CompletableFuture, e.g. CompletableFuture.supplyAsync(Supplier), run tasks on the common ForkJoinPool which would break the Vertx contract. Open-source-software to the rescue, there is a solution to this problem: VertxCompletableFuture. This special implementation guarantees that async operations run on a Vertx context unless you explicitly specify an Executor in one of the overloaded xyzAsync-methods*.

And here comes even better news: starting from version 2, vertx-jooq also supports this way of dealing with asynchronous database operations by utilizing VertxCompletableFuture. Checkout the new vertx-jooq-future module and the according code generator to create your CompletableFuture-based DAOs.

* There have been discussions in the Vertx developer group about a CompletableFuture based API, e.g. here and especially here. The current status is that they do not provide such API officially, mostly because VertxCompletableFuture breaks the contract of the supplyAsync-methods, since it runs within the Vertx context and not the ForkJoinPool. Also when you pass this CompletableFuture subclass to code that expects a regular CompletableFuture, it breaks the Liskov substitution principle and OOP (thanks for pointing that out in the comments Julien). My opinion is, that if you are using Vertx you are aware of the special threading model and can tolerate that behavior. But, of course, it’s up to you.