從0到1學習node(八)之異步控制工具async

系列索引: https://www.xiabingbao.com/node/2017/01/08/node-list.html
本文地址: https://www.xiabingbao.com/node/2017/02/25/node-async.html html

咱們在編寫異步程序時,最頭痛的就是不知道結果何時返回給咱們,而後執行後面的操做,不少時候只能把後面的操做放到返回成功的函數裏,或者使用計數器等方法。 前端

比較典型的兩個就是:後面的操做須要依賴上一個異步操做的結果;多個異步操做並行執行,都執行完成後再執行接下來的操做。這兩個操做中,第一個異步的程序咱們可能會寫成這樣:node

db.select(SQL1, function(res1){
    db.delete(SQL2, function(res2){
        db.insert(SQL3, function(res3){
            // ...
        })
    })
});

將後面的操做寫到執行成功後的回調函數裏。第2個並行的異步操做,可使用計數器的方法,每一個異步調用成功時,計數器加1,當全部的異步都調用成功後,再接着執行:git

var count = 0;
var success = function(){
    count++;
    if(count>=3){ 
        console.log('執行完畢...');
    }
}

var select = function(){
    db.select(sql, function(res){
        success();
    })
}
var select2 = function(){
    db.select(sql, function(res){
        success();
    })
}
var select3 = function(){
    db.select(sql, function(res){
        success();
    })
}
select();
select2();
select3();

這些編寫方式很是麻煩,並且代碼邏輯比較混亂,調試起來也很不方便。那麼就要用到異步控制的利器async了。github

1. 介紹

async的做用是進行流程的控制,並且提供了很是多的方法可供調用。這些方法能夠分爲三大類:ajax

  • 集合類(Collections)sql

  • 流程控制類(Control Flow)json

  • 工具類(Utils)數組

下面咱們從這三個分類裏分別挑出幾個方法進行講解。微信

2. 函數介紹

async中提供了很是多的方法可供使用,咱們僅僅是講解其中幾個比較有表明性的,其餘的能夠訪問官方文檔:http://caolan.github.io/async/docs.html

2.1 集合類

集合類中的方法主要有some, 'map', 'each', 'every'等,這些方法是對數組或組合進行某個相同的操做後,統一執行回調函數。

咱們以map爲例,map對集合中的每個元素,執行某個相同的異步操做,獲得結果。全部的結果將彙總到最終的callback裏。

使用方法,map接收三個參數,分別是:

參數名稱 類型 說明
coll iteratee callback
Array | Iterable | Object function function
須要處理數組,集合或其餘可迭代的類型 迭代方法,用來對集合中的每一項進行處理。該方法接收兩個參數(item, callback);item爲集合中的每一項, callback爲回調函數。callback須要帶有err(有時可能爲null)和處理後的數據,callback(err, data) 最終回調函數,當集合處理完畢後調用此函數,傳遞兩個參數err和result,result爲以前處理後的全部的結果的集合

注意:中間處理函數iteratee對coll中的每一項都是併發處理的,所以並不能保證iteratee按照順序完成。不過,若是coll是個數組,最後的結果集results會按照coll中的順序排列;若是coll是個集合(Object)類型,results會是數組類型,結果將大體按照coll的鍵的順序排列(可是不一樣在不一樣的JavaScript引擎中會有可能發生變化)。

咱們來舉個例子,使用map獲取幾個文件中的內容:

var files = ['./file/cnode_1.txt', './file/cnode_2.txt', './file/cnode_3.txt'];

// 讀取文件內容
// 第1個參數 文件名稱列表的數組
// 第2個參數 傳入數組中的每一項和回調函數
// 第3個參數 results爲全部結果的集合
async.map(files, function(file, cb){
    fs.readFile(file, 'utf-8', function(err, data){
        cb(err, data);
    })
}, function(err, results){
    console.log( results );
})

並且,若是中間的處理函數比較大,不想寫在map中,也能夠單獨寫成一個函數,而後傳遞進去,不過參數傳遞仍是要符合規則的:

var files = ['./file/cnode_1.txt', './file/cnode_2.txt', './file/cnode_3.txt'];

var read = function(file, cb){
    fs.readFile(file, 'utf-8', function(err, data){
        cb(err, data);
    })
}
async.map(files, read, function(err, result){
    console.log( result );
})

這裏還有一個mapLimit,能夠傳遞一個參數limit,用來限制併發的數量:mapLimit(coll, limit, iteratee, callbackopt):

// 併發數量爲2
async.mapLimit(files, 2, read, function(err, result){
    console.log( result );
})

同時,集合類中還有其餘的方法,咱們也稍微瞭解下:

  • each : 與map相似,可是最後的回調函數裏沒有results,each只循環不負責處理結果

  • every : 中間處理函數iteratee的參數(err, boolean)須要傳遞一個boolean值,若全部選項的結果都爲true,則results爲true

  • some : 與every相似,只是只要其中一個選項的結果爲true,則results爲true

  • filter : 對coll進行篩選,篩選出結果爲true的結果

  • reject : 與filter正好相反,篩選出結果爲false的結果

  • concat : 將每一個異步操做的結果合併爲一個數組

2.2 流程控制類

上面的集合類是對一個集合進行相同的處理,集合中的每一項都處理完後,再對結果進行回調處理。而多個回調方法執行時,則須要對這幾個回調方法進行控制了。

多個回調方法執行時,一般有這麼幾個流程:

  1. 串行且無關聯,即執行完一個後再依次執行下一個,且相互之間無數據交互,都執行完後,再執行最後的回調函數。可使用async.series

  2. 串行且有關聯,即執行完一個後再依次執行下一個,且上一個回調函數的結果會做爲下一個回調函數的參數。可使用async.waterfall

  3. 並行,這幾個回調函數同時併發執行,都執行完成後,再執行最後的回調函數。可使用async.parallel

固然還有其餘更復雜的流程,這裏也只聊上面的三種狀況。

async.seriesasync.waterfallasync.parallel的語法都是同樣的:

async.Method(coll, function(err, results){

})

其中coll既能夠是數組,也能夠是json格式的,並且results的類型與coll對應。

串行且無關聯async.series

// 串行且無關聯,數組格式
async.series([
    function(cb){
        getAllList(function(result){
            cb(null, result);
        });
    },
    function(cb){
        getAllUser(function(result){
            cb(null, result);
        });
    }
], function(err, result){
    console.log(result);
})

同時串行的異步能夠是json格式的:

// 串行且無關聯,json個數
async.series({
    one: function(cb){
        getAllList(function(result){
            cb(null, result);
        });
    },
    two: function(cb){
        getAllUser(function(result){
            cb(null, result);
        });
    }
}, function(err, result){
    console.log(result);
})

串行且有關聯async.waterfall

// 串行且上一個結果做爲下一個的參數
async.waterfall([
    function(cb){
        getListById(1, function(result){
            cb(null, result);
        });
    },
    function(params, cb){
        console.log(params);
        getAllUser(function(result){
            cb(null, result);
        });
    }
], function(err, result){
    console.log(result);
})

並行async.parallel

// 並行,getAllList與getAllUser同時執行
async.parallel([
    function(cb){
        getAllList(function(result){
            cb(null, result);
        });
    },
    function(cb){
        getAllUser(function(result){
            cb(null, result);
        });
    }
], function(err, result){
    console.log(result);
})

關於並行的異步操做,這裏還有一個async.parallelLimit,限制併發的數量:

// 併發數量爲2
async.parallelLimit([
    iteratee1, iteratee2, iteratee3, ...
], 2, function(err, results){
    
})

2.3 工具類

async中也提供了很多的工具方法可供使用,好比async.log能夠輸出回調方法中的值,第1個參數爲函數,後面的參數爲傳遞給函數的參數:

var hello = function(name, callback) {
    setTimeout(function() {
        callback(null, 'hello ' + name);
    }, 1000);
};

// 將'world'傳遞給hello方法
async.log(hello, 'world'); // 'hello world'

這裏面還有apply, dir, timeout等方法。

3. 總結

使用async控制異步流程很是的方便,並且也能夠在前端使用,好比能夠操做多個ajax請求等。

系列索引: https://www.xiabingbao.com/node/2017/01/08/node-list.html
本文地址: https://www.xiabingbao.com/node/2017/02/25/node-async.html

歡迎你們關注個人微信公衆號:
qrcode

相關文章
相關標籤/搜索