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

Vert.x乃神器也,管玩管住還包飯,異步處理,很多人頭疼的callback hell(回調(diào)地獄),也就是金字塔,不知不覺(jué)就走入了金字塔的陷阱里,一般callback都發(fā)生在異步處理期間,而異步處理,除了少數(shù)數(shù)據(jù)量極大的cpu intense的代碼塊以外(這部分主要通過(guò)降低算法時(shí)間復(fù)雜度來(lái)優(yōu)化),絕大多數(shù)都集中在io上,將一個(gè)io操作包裝成一個(gè)異步程序塊來(lái)執(zhí)行,是比較常見(jiàn)的優(yōu)化方式,vert.xio集中在以下幾個(gè)部分。

第一,eventbus.send()方法。

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

第三,httpclient.get/post等方法,其中前兩個(gè)都可以通過(guò)vert.x自帶的Future來(lái)對(duì)付。

首先我們要實(shí)例化一個(gè)future對(duì)象:

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

然后future有幾個(gè)方法值得注意,其中一個(gè)是setHandler方法,這個(gè)方法就是設(shè)置后續(xù)處理的函數(shù),另外一個(gè),與之相對(duì)應(yīng)的是completer()方法,這個(gè)方法返回的就是setHandler方法sethandler函數(shù),通俗點(diǎn)說(shuō),就是getHandler()方法,在callback位置調(diào)用該方法,就可以在callback的時(shí)候執(zhí)行handler方法/函數(shù),比如原本是:

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

經(jīng)過(guò)改寫(xiě)之后,就變成了:

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

這樣通過(guò)拆分原來(lái)的內(nèi)嵌lambda/匿名函數(shù)來(lái)達(dá)到扁平化的效果。
callback hell一個(gè)典型特征就是金字塔,也就是經(jīng)過(guò)多層回調(diào)/callback函數(shù)嵌套之后,程序會(huì)變成:

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

這種方式,或者fp里面常見(jiàn)的(+ 1 (+ 1 (+ 1 1)));如果強(qiáng)行format的話,就會(huì)變成一個(gè)多層多次縮進(jìn)的,向右凸起的金字塔,極為丑陋,但是如果不這么做,又無(wú)法保證順序,所以為了保證順序的同時(shí),我們要扁平化該金字塔,那么如何保證順序呢?
compose方法,比如:

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

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

那么當(dāng)fut1被觸發(fā)之后,如果成功,則執(zhí)行compose方法里面的第一個(gè)參數(shù),也就是那個(gè)回調(diào)函數(shù)/handler,如果失敗,則直接傳遞給compose方法的第二個(gè)參數(shù),也就是fut2,依次類(lèi)推,便可以很輕松地組裝出你想要的扁平化鏈條,使得多層縮進(jìn)的噩夢(mèng)不再。
利用這種方式,可以扁平化處理vertx.eventBus()jdbcClient.getConnection()等包含有異步callback的代碼塊。
至于Vert.x自帶的httpclient,這個(gè)稍微有些特殊,因?yàn)閳?zhí)行get/post等方法的時(shí)候,response handler并不能囊括全部情況,尤其是常見(jiàn)的超時(shí)/timeout,屬于exception范疇,也就是說(shuō),要想囊括全部返回情況,至少需要responsebody handler,同時(shí)也需要一個(gè)exception handler,最后還要調(diào)用end方法才能將全部常見(jiàn)的情況全部覆蓋住,代碼如下:

client1.get(port, ip, url, response -> {
    response.bodyHandler(respBody -> {
    //這里處理正常返回
    });
}).exceptionHandler(exception -> {
    //這里處理超時(shí)
}).end();

如果要在這里面用future的話,需要顯式處理標(biāo)識(shí)成功future.complete(...)或者失敗future.fail(...)
另外,常見(jiàn)的httpClient.getNow();等方法相當(dāng)于:

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

也就是沒(méi)有exception handler的普通get/post等方法+end方法,所以如果用getNow等方法發(fā)送請(qǐng)求,會(huì)無(wú)法捕捉處理timeout超時(shí)異常。

通過(guò)這篇文章想必對(duì)Vertx如何避免callback hell有所了解。

全科龍婷▼升職加薪

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容