Vert.x 3.x版本callback hell的解决

Vert.x乃神器也,管玩管住还包饭,异步处理,很多人头疼的callback hell(回调地狱),也就是金字塔,不知不觉就走入了金字塔的陷阱里,一般callback都发生在异步处理期间,而异步处理,除了少数数据量极大的cpu intense的代码块以外(这部分主要通过降低算法时间复杂度来优化),绝大多数都集中在io上,将一个io操作包装成一个异步程序块来执行,是比较常见的优化方式,vert.xio集中在以下几个部分。

第一,eventbus.send()方法。

第二,jdbcclient.getconnection以及connection.update/query等方法。

第三,httpclient.get/post等方法,其中前两个都可以通过vert.x自带的Future来对付。

首先我们要实例化一个future对象:

Future<MyResult> future = Future.future();

然后future有几个方法值得注意,其中一个是setHandler方法,这个方法就是设置后续处理的函数,另外一个,与之相对应的是completer()方法,这个方法返回的就是setHandler方法sethandler函数,通俗点说,就是getHandler()方法,在callback位置调用该方法,就可以在callback的时候执行handler方法/函数,比如原本是:

vertx.eventBus().send(CommerceVerticle.class.getName(),myAsyncResult -> {...});

经过改写之后,就变成了:

vertx.eventBus().send(CommerceVerticle.class.getName(),future.completer());
future.setHandler(myAsyncResult -> {...});

这样通过拆分原来的内嵌lambda/匿名函数来达到扁平化的效果。
callback hell一个典型特征就是金字塔,也就是经过多层回调/callback函数嵌套之后,程序会变成:

ar -> {
    ar1 -> {
         ar2 -> {
             ...
         }
    }
};

这种方式,或者fp里面常见的(+ 1 (+ 1 (+ 1 1)));如果强行format的话,就会变成一个多层多次缩进的,向右凸起的金字塔,极为丑陋,但是如果不这么做,又无法保证顺序,所以为了保证顺序的同时,我们要扁平化该金字塔,那么如何保证顺序呢?
compose方法,比如:

Future fut1 = Future.future();
Future fut2 = Future.future();
fut1.compose(asyncMyResult -> {
    ...
    fut2.complete();
    ...
}, fut2);

fut2.setHandler(asyncMyResult2 -> {
    ...
});

那么当fut1被触发之后,如果成功,则执行compose方法里面的第一个参数,也就是那个回调函数/handler,如果失败,则直接传递给compose方法的第二个参数,也就是fut2,依次类推,便可以很轻松地组装出你想要的扁平化链条,使得多层缩进的噩梦不再。
利用这种方式,可以扁平化处理vertx.eventBus()jdbcClient.getConnection()等包含有异步callback的代码块。
至于Vert.x自带的httpclient,这个稍微有些特殊,因为执行get/post等方法的时候,response handler并不能囊括全部情况,尤其是常见的超时/timeout,属于exception范畴,也就是说,要想囊括全部返回情况,至少需要responsebody handler,同时也需要一个exception handler,最后还要调用end方法才能将全部常见的情况全部覆盖住,代码如下:

client1.get(port, ip, url, response -> {
    response.bodyHandler(respBody -> {
    //这里处理正常返回
    });
}).exceptionHandler(exception -> {
    //这里处理超时
}).end();

如果要在这里面用future的话,需要显式处理标识成功future.complete(...)或者失败future.fail(...)
另外,常见的httpClient.getNow();等方法相当于:

client1.get(port, ip, url, response -> {
    response.bodyHandler(respBody -> {
    //这里处理正常返回
    });
}).end();

也就是没有exception handler的普通get/post等方法+end方法,所以如果用getNow等方法发送请求,会无法捕捉处理timeout超时异常。

通过这篇文章想必对Vertx如何避免callback hell有所了解。

参考此文编辑 非常感谢。