本文主要講解async的函數(shù): series,parallel, waterfall 函數(shù)的理解和 使用
async 函數(shù)是什么?
一句話,async 函數(shù)就是 Generator 函數(shù)的語法糖。
背景:
Node.js 使用事件驅(qū)動(dòng),非阻塞I/O 模型而得以輕量和高效。
事件發(fā)生后交由指定的程序處理,處理完成后就調(diào)用事件回調(diào)函數(shù)。回調(diào)機(jī)制使用Node.js具有了強(qiáng)大的并發(fā)處理能力,但也帶來了一系列的回調(diào)嵌套問題。
async解決了什么問題?
解決回調(diào)嵌套的方法有很多,如:async、promise、事件發(fā)射器等。幾種方案多少都使用過,今天介紹一下async中的常用方法。本篇主要介紹其流程控制部分。
async 提供的api包括三個(gè)部分::
- 流程控制
- 集合處理
- 工具
流程控制常用的方法有:
series、parallel、waterfall、whilst、auto等。
這些方法功能如下:
- series:多個(gè)任務(wù)依次執(zhí)行
- parallel:多個(gè)任務(wù)并發(fā)執(zhí)行
- waterfall:多個(gè)任務(wù)依次執(zhí)行,上一任務(wù)的輸出可做為下一任務(wù)的輸入?yún)?shù)
- whilst:while循環(huán)執(zhí)行任務(wù),但本次任務(wù)執(zhí)行完畢后才會(huì)進(jìn)入下一次循環(huán)
- auto:根據(jù)任務(wù)需要選擇順序或并發(fā)執(zhí)行任務(wù)
下面重點(diǎn)介紹series、parallel、waterfall:
安裝方法: npm install async
使用方法: var async=require('async');
1.Async.series(tasks, [callback])
- 用法:
series方法用于依次執(zhí)行(串行執(zhí)行)多個(gè)方法
一個(gè)方法執(zhí)行完畢后才會(huì)進(jìn)入下一方法
方法之間沒有數(shù)據(jù)傳遞
- 參數(shù)
- tasks:需要執(zhí)行多個(gè)方法。tasks可以以數(shù)組形式傳入,也可以以object對(duì)象形式傳入。參數(shù)類型不同,影響的是返回?cái)?shù)據(jù)的格式。每個(gè)方法都要一個(gè)回調(diào)方法
-
callback(err, result),用于處理錯(cuò)誤或進(jìn)入下一方法。當(dāng)發(fā)生錯(cuò)誤時(shí)(即:err參數(shù)存在時(shí)),其后的方法會(huì)跳過,錯(cuò)誤被傳入最終回調(diào)方法中。
callback(err, results):可選的最終回調(diào)方法。出錯(cuò)時(shí),tasks中拋出的錯(cuò)誤將在此方法中捕獲,錯(cuò)誤被傳入err參數(shù)。不出錯(cuò)時(shí),tasks中回調(diào)結(jié)果將被寫入results參數(shù)中,以數(shù)據(jù)或?qū)ο笮问教峁?/li>
- 實(shí)例
var async = require('async');
//以數(shù)組形式傳入需要執(zhí)行的多個(gè)方法
async.series([
function(callback){
// 執(zhí)行一些操作后,callback進(jìn)入下一方法
callback(null, 'one');
},
function(callback){
// 執(zhí)行一些操作后,callback進(jìn)入可選的最終回調(diào)方法
callback(null, 'two');
}
],
// 可選的最終回調(diào)
function(err, results){
// 當(dāng)tasks中的任一方法發(fā)生錯(cuò)誤,即回調(diào)形式為callback('錯(cuò)誤信息')時(shí),錯(cuò)誤將被傳遞給err參數(shù),未發(fā)生錯(cuò)誤err參數(shù)為空
// results中為數(shù)組中兩個(gè)方法的結(jié)果數(shù)組:['one', 'two']
});
//以object對(duì)象形式傳入需要執(zhí)行的多個(gè)方法
async.series({
one: function(callback){
// 執(zhí)行一些操作后,callback進(jìn)入下一方法
callback(null, 1);
},
two: function(callback){
// 執(zhí)行一些操作后,callback進(jìn)入可選的最終回調(diào)方法
callback(null, 2);
}
},
function(err, results) {
// 當(dāng)tasks中的任一方法發(fā)生錯(cuò)誤,即回調(diào)形式為callback('錯(cuò)誤信息')時(shí),錯(cuò)誤將被傳遞給err參數(shù),未發(fā)生錯(cuò)誤err參數(shù)為空
// results中為數(shù)組中兩個(gè)方法的結(jié)果對(duì)象:{one: 1, two: 2}
});
該函數(shù)的詳細(xì)解釋為:
- 依次執(zhí)行一個(gè)函數(shù)數(shù)組中的每個(gè)函數(shù),每一個(gè)函數(shù)執(zhí)行完成之后才能執(zhí)行下一個(gè)函數(shù)。
- 如果任何一個(gè)函數(shù)向它的回調(diào)函數(shù)中傳了一個(gè)error,則后面的函數(shù)都不會(huì)被執(zhí)行,并且將會(huì)立刻會(huì)將該error以及已經(jīng)執(zhí)行了的函數(shù)的結(jié)果,傳給series中最后那個(gè)callback。
- 當(dāng)所有的函數(shù)執(zhí)行完后(沒有出錯(cuò)),則會(huì)把每個(gè)函數(shù)傳給其回調(diào)函數(shù)的結(jié)果合并為一個(gè)數(shù)組,傳給series最后的那個(gè)callback。
- 還可以json的形式來提供tasks。每一個(gè)屬性都會(huì)被當(dāng)作函數(shù)來執(zhí)行,并且結(jié)果也會(huì)以json形式傳給series最后的那個(gè)callback。這種方式可讀性更高一些。
- 需要注意的是:
如果中間某個(gè)函數(shù)出錯(cuò),series函數(shù)如何處理
思考一下:如果某個(gè)函數(shù)傳給回調(diào)的值為undefined, null, {}, []等,series如何處理
另外還需要注意的是:多個(gè)series調(diào)用之間是不分先后的,因?yàn)閟eries本身也是異步調(diào)用。
2. parallel(tasks, [callback])
- 執(zhí)行原理
parallel方法用于 并行執(zhí)行 多個(gè)方法,所有傳入的方法都是立即執(zhí)行,方法之間沒有數(shù)據(jù)傳遞。傳遞給最終callback的數(shù)組中的數(shù)據(jù)按照tasks中聲明的順序,而不是執(zhí)行完成的順序。
- 參數(shù)
- ** tasks:需要執(zhí)行多個(gè)方法。tasks可以以 *** 數(shù)組形式傳入,也可以以object對(duì)象*形式傳入。和series函數(shù)一樣,tasks參數(shù)類型不同,返回的results格式會(huì)不一樣。
每個(gè)方法都要一個(gè)回調(diào)方法callback(err, result),回調(diào)方法需要提供一個(gè)err參數(shù)或是result參數(shù)。
當(dāng)如果某個(gè)函數(shù)出錯(cuò),則立刻將err和已經(jīng)執(zhí)行完的函數(shù)的結(jié)果值傳給parallel最終的callback。其它未執(zhí)行完的函數(shù)的值不會(huì)傳到最終數(shù)據(jù),但要占個(gè)位置。
- callback(err, results):可選的最終回調(diào)方法。出錯(cuò)時(shí),tasks中拋出的錯(cuò)誤將在此方法中捕獲,錯(cuò)誤被傳入err參數(shù)。不出錯(cuò)時(shí),tasks中回調(diào)結(jié)果將被寫入results參數(shù)中,以數(shù)據(jù)或?qū)ο笮问教峁?/li>
- 示例
//以數(shù)組形式傳入需要執(zhí)行的多個(gè)方法
async.parallel([
function(callback){
// 執(zhí)行一些操作后,callback表示本方法執(zhí)行完成
callback(null, 'one');
},
function(callback){
// 執(zhí)行一些操作后,callback表示本方法執(zhí)行完成
callback(null, 'two');
}
],
// 可選的最終回調(diào)
function(err, results){
// 當(dāng)tasks中的任一方法發(fā)生錯(cuò)誤,即回調(diào)形式為callback('錯(cuò)誤信息')時(shí),錯(cuò)誤將被傳遞給err參數(shù),未發(fā)生錯(cuò)誤err參數(shù)為空
// results中為數(shù)組中兩個(gè)方法的結(jié)果數(shù)組:['one', 'two'] ,即使第二個(gè)方法先執(zhí)行完成,其結(jié)果也是在第一個(gè)方法結(jié)果之后
});
//以object對(duì)象形式傳入需要執(zhí)行的多個(gè)方法
async.parallel({
one: function(callback){
// 執(zhí)行一些操作后,callback表示本方法執(zhí)行完成
callback(null, 1);
},
two: function(callback){
// 執(zhí)行一些操作后,callback表示本方法執(zhí)行完成
callback(null, 2);
}
},
function(err, results) {
// 當(dāng)tasks中的任一方法發(fā)生錯(cuò)誤,即回調(diào)形式為callback('錯(cuò)誤信息')時(shí),錯(cuò)誤將被傳遞給err參數(shù),未發(fā)生錯(cuò)誤err參數(shù)為空
// results中為數(shù)組中兩個(gè)方法的結(jié)果對(duì)象:{one: 1, two: 2}
});
3. waterfall(tasks, [callback])
- 用法
waterfall方法與series方法類似用于依次執(zhí)行多個(gè)方法,一個(gè)方法執(zhí)行完畢后才會(huì)進(jìn)入下一方法
不同與series方法的是:
waterfall之間有數(shù)據(jù)傳遞。
waterfall的多個(gè)方法只能以數(shù)組形式傳入,不支持object對(duì)象。
- 參數(shù)
-
tasks:需要執(zhí)行多個(gè)方法。tasks只能以數(shù)組形式傳入。每個(gè)方法都要一個(gè)回調(diào)方法callback(err, result1, result2, ...),用于處理錯(cuò)誤或進(jìn)入下一方法。
當(dāng)發(fā)生錯(cuò)誤時(shí)(即:err參數(shù)存在時(shí)),其后的方法會(huì)跳過,錯(cuò)誤被傳入最終回調(diào)方法中。無錯(cuò)誤時(shí)回調(diào)參數(shù)result1, result2……將做為下一方法的輸入?yún)?shù)
callback(err, results):可選的最終回調(diào)方法。出錯(cuò)時(shí),tasks中拋出的錯(cuò)誤將在此方法中捕獲,錯(cuò)誤被傳入err參數(shù)。不出錯(cuò)時(shí),tasks中回調(diào)結(jié)果results為最后一個(gè)方法的回調(diào)結(jié)果。
- 示例
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 現(xiàn)在是 'one', arg2 現(xiàn)在是 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 現(xiàn)在是 'three'
callback(null, 'done');
}
], function (err, result) {
//執(zhí)行的任務(wù)中方法回調(diào)err參數(shù)時(shí),將被傳遞至本方法的err參數(shù)
// 參數(shù)result為最后一個(gè)方法的回調(diào)結(jié)果'done'
});