1. 程式人生 > >node.js async實踐分享

node.js async實踐分享

nodejs是基於事件驅動的,所有的一切都是非同步呼叫的,這種實現機制優點確實十分明顯,那就是避免了同步呼叫的無盡等待。一直以來,node.js用著都是比較令人感覺興奮的,直到有一天,我陷入了它埋藏的無盡的回撥深坑中,讓我內牛滿面。

記得那是一個秋天,大約在冬季,我在我的程式碼小王國裡做一件小事。我都覺得這個事情實在太小了,小的都不好意思說,不過我還是要說,我要在redis快取中找jiangcs520這個哥們養的一條狗狗,事情是這樣的,redis裡面存了張使用者表,記錄了使用者有哪些朋友,還存了一張人-狗表(沒有罵人的意思哦),其實是記錄了每一使用者養了那條狗,還有一張狗狗表,記錄了狗狗的nickname,再給大家整理一下,總共三張表:user表,user_Dog表,dog表。那我為什麼要找這樣一條狗狗呢,事情是這樣的,春天來了,狗狗的春天也來了,然後有個朋友(男)對博主(男)說,大家朋友多年,情同手足,雖然我們性別不和,不過我們兩家狗狗的性別合適,要不結個親,這事就說定了,狗狗成親那天,那條美女狗狗來了,博主的狗狗還在redis中存著呢,肯定要先喊出來啦,以前,用java的時候,好方便啊,一下就找出來了,先從user表中找到博主jiangcs520,然後從user_Dog表中找到他養的帥狗狗名字,然後在dog表中找到那條狗狗,牽出來就好咯。但是在node.js中就不是這樣啦,雖然是同樣的順序,但是大家都懂的,是非同步啦,結果都是在回撥函式中返回,虛擬碼如下:

dbDAO.select(usersql,function(err,users){ //第一層查詢使用者表
    if(err){
        logger.error(err);
    }else{
        if(users){
            dbDAO.select(user_dogSql,function(err,user_dogs){//第二層查詢使用者-狗狗表
                if(user_dogs){
                    dbDAO.select((dogSql,function(err,dogs){//第三層查詢狗狗表
                        if(dogs){
                            "找到狗狗了,牽出來啦"
                        }
                    }))
                }
            })
        }
    }
});

node.js中,就需要如上三層的回撥,如此簡單的一個查詢,就需要三層回撥,那要是我還要查詢狗狗的親戚,狗狗生出的狗仔的另一半的親戚,那怎麼辦,得套幾層啊,幾百行程式碼全是回撥的,誰不暈倒,node還有人用嗎。就在這個危難關頭,回撥世界救世主出現了,它就是“async”,粉墨登場。

如上的問題,async是如何解決的呢,看看救世主是如何拯救回調大軍的,偽碼如下:

async.waterfall([
    function(cb){
        //查詢使用者表
        dbDAO.select(usersql,function(err,users){
            cb(err,results);
        });
        
    },function(results,cb){
        //查詢使用者_狗狗表
        dbDAO.select(user_dogSql,function(err,users){
            cb(err,results);
        });
    },function(results,cb){
        //查詢狗狗表
        dbDAO.select(dogSql,function(err,users){
            cb(err,results);
        });
    }
],function(err,results){
    //查到狗狗了,可以牽出來了
})


是不是覺得看著更有層次感,更加清晰。這裡是使用了async的瀑布流waterfull方法,第一個引數就是需要執行的方法陣列,第二個引數就是一個回撥函式,觀察一下這個函式陣列,每一個函式結構都是類似的,首先看第一個函式,有一個引數cb,其實這個引數是async自己內部指定的,當我們在函式體內處理完了我們需要處理的事情,就需要呼叫這個函式,如上方式cb(err,results),再來看第二個函式,與第一個函式有所區別,有兩個引數,results和cb,其實這個results是上一函式處理完的結果傳遞下來的,是不是很像瀑布一樣,所以才命名為瀑布流的吧。第三個函式就不講了,同樣道理。這裡要注意cb回撥函式很重要,若cb忘了執行或者是某個cb回撥未執行,那麼,這個瀑布流就無法繼續下去了,waterfull中的回撥函式也不會執行。還有一種情況,若其中某個函式中出現了錯誤,接下來的函式不會繼續執行,在呼叫了當前的cb回撥函式後,會直接進入waterfull的回撥函式中。比如我們在查詢使用者_狗狗表的時候,處理完後忘了呼叫回撥函式cb,那麼,故事就到此結束了,不會繼續往下走了,若我們都沒忘記這一茬,但是資料查詢的時候出錯了,那麼接下來的查詢狗狗表也就沒有了,但是與前一種情況的區別是會進入waterfull的回撥函式,錯誤資訊err就作為引數傳遞了。

async還有幾個常用的方法:series,parallel,auto方法等等。在業務邏輯較為複雜的情況下,這些方法都是十分好用的。series顧名思義比較好理解,順序執行,按照函式陣列的順序執行所有方法,parallel是平行的意思,就是同時執行的意思,那麼具體哪個函式先返回就不好說了,但是回撥函式中返回的結果是按照函式陣列的順序返回的。更加具體詳細的使用方法再給大家推薦一篇博文:http://www.verydemo.com/demo_c441_i206465.html

最後提醒一個容易粗錯的地方,當大家檢查完程式碼都沒有問題,但是最終回撥函式始終沒有執行的時候,可以檢查一下,函式陣列中的所有函式執行完後是否都已經呼叫cb,若少了任意一個,都是無法執行到最後的。

對已async的實踐還不多,吐槽拍磚請隨意。