Async 詳解

一:流程控制

為了適應異步編程,減少回調的嵌套,我嘗試了很多庫。最終覺得還是async最靠譜。

地址:https://github.com/caolan/async

Async的內容分為三部分:

流程控制:簡化十種常見流程的處理

集合處理:如何使用異步操作處理集合中的數(shù)據(jù)

工具類:幾個常用的工具類

本文介紹其中最簡單最常用的流程控制部分。

由于nodejs是異步編程模型,有一些在同步編程中很容易做到的事情,現(xiàn)在卻變得很麻煩。Async的流程控制就是為了簡化這些操作。

1. series(tasks, [callback]) (多個函數(shù)依次執(zhí)行,之間沒有數(shù)據(jù)交換)

有多個異步函數(shù)需要依次調用,一個完成之后才能執(zhí)行下一個。各函數(shù)之間沒有數(shù)據(jù)的交換,僅僅需要保證其執(zhí)行順序。這時可使用series。

純js代碼:

step1(function(err, v1) {

step2(function(err, v2) {

step3(function(err, v3) {

// do somethig with the err or values v1/v2/v3

}

}

});

從中可以看到這嵌套還是比較多深的,如果再多幾步,會更深。在代碼中忽略對了每一層err的處理,否則還都等加上 if(err) return callback(err),那就更麻煩了。

對于這種情況,使用async來處理,就是這樣的:

varasync= require('async')

async.series([

step1, step2, step3

], function(err, values) {

// do somethig with the err or values v1/v2/v3

});

可以看到代碼簡潔了很多,而且自動處理每個回調中的錯誤。當然,這里只給出來最最簡單的例子,在實際中,我們常會在每個step中執(zhí)行一些操作,這時可寫成:

varasync= require('async')

async.series([

function(cb) { step1(function(err,v1) {

// do something with v1

cb(err, v1);

}),

function(cb) { step2(...) },

function(cb) { step3(...) }

], function(err, values) {

// do somethig with the err or values v1/v2/v3

});

該函數(shù)的詳細解釋為:

依次執(zhí)行一個函數(shù)數(shù)組中的每個函數(shù),每一個函數(shù)執(zhí)行完成之后才能執(zhí)行下一個函數(shù)。

如果任何一個函數(shù)向它的回調函數(shù)中傳了一個error,則后面的函數(shù)都不會被執(zhí)行,并且將會立刻會將該error以及已經執(zhí)行了的函數(shù)的結果,傳給series中最后那個callback。

當所有的函數(shù)執(zhí)行完后(沒有出錯),則會把每個函數(shù)傳給其回調函數(shù)的結果合并為一個數(shù)組,傳給series最后的那個callback。

還可以json的形式來提供tasks。每一個屬性都會被當作函數(shù)來執(zhí)行,并且結果也會以json形式傳給series最后的那個callback。這種方式可讀性更高一些。

具體例子可參考:https://github.com/freewind/async_demo/blob/master/series.js

其代碼中還包含了:

如果中間某個函數(shù)出錯,series函數(shù)如何處理

如果某個函數(shù)傳給回調的值為undefined, null, {}, []等,series如何處理

另外還需要注意的是:多個series調用之間是不分先后的,因為series本身也是異步調用。

2. parallel(tasks, [callback]) (多個函數(shù)并行執(zhí)行)

并行執(zhí)行多個函數(shù),每個函數(shù)都是立即執(zhí)行,不需要等待其它函數(shù)先執(zhí)行。傳給最終callback的數(shù)組中的數(shù)據(jù)按照tasks中聲明的順序,而不是執(zhí)行完成的順序。

如果某個函數(shù)出錯,則立刻將err和已經執(zhí)行完的函數(shù)的結果值傳給parallel最終的callback。其它未執(zhí)行完的函數(shù)的值不會傳到最終數(shù)據(jù),但要占個位置。

同時支持json形式的tasks,其最終callback的結果也為json形式。

示例代碼:

async.parallel([

function(cb) { t.fire('a400', cb, 400) },

function(cb) { t.fire('a200', cb, 200) },

function(cb) { t.fire('a300', cb, 300) }

], function (err, results) {

log('1.1 err: ', err); // -> undefined

log('1.1 results: ', results); // ->[ 'a400', 'a200', 'a300' ]

});

中途出錯的示例:

async.parallel([

function(cb) { log('1.2.1: ', 'start'); t.fire('a400', cb, 400) }, // 該函數(shù)的值不會傳給最終callback,但要占個位置

function(cb) { log('1.2.2: ', 'start'); t.err('e200', cb, 200) },

function(cb) { log('1.2.3: ', 'start'); t.fire('a100', cb, 100) }

], function(err, results) {

log('1.2 err: ', err); // -> e200

log('1.2 results: ', results); // -> [ , undefined, 'a100' ]

});

以json形式傳入tasks

async.parallel({

a: function(cb) { t.fire('a400', cb, 400) },

b: function(cb) { t.fire('c300', cb, 300) }

}, function(err, results) {

log('1.3 err: ', err); // -> undefined

log('1.3 results: ', results); // -> { b: 'c300', a: 'a400' }

});

更詳細示例參見:https://github.com/freewind/async_demo/blob/master/parallel.js

3. waterfall(tasks, [callback]) (多個函數(shù)依次執(zhí)行,且前一個的輸出為后一個的輸入)

與seires相似,按順序依次執(zhí)行多個函數(shù)。不同之處,每一個函數(shù)產生的值,都將傳給下一個函數(shù)。如果中途出錯,后面的函數(shù)將不會被執(zhí)行。錯誤信息以及之前產生的結果,將傳給waterfall最終的callback。

這個函數(shù)名為waterfall(瀑布),可以想像瀑布從上到下,中途沖過一層層突起的石頭。注意,該函數(shù)不支持json格式的tasks。

async.waterfall([

function(cb) { log('1.1.1: ', 'start'); cb(null, 3); },

function(n, cb) { log('1.1.2: ',n); t.inc(n, cb); },

function(n, cb) { log('1.1.3: ',n); t.fire(n*n, cb); }

], function (err, result) {

log('1.1 err: ', err); // -> null

log('1.1 result: ', result); // -> 16

});

更詳細示例參見:https://github.com/freewind/async_demo/blob/master/waterfall.js

4. auto(tasks, [callback]) (多個函數(shù)有依賴關系,有的并行執(zhí)行,有的依次執(zhí)行)

用來處理有依賴關系的多個任務的執(zhí)行。比如某些任務之間彼此獨立,可以并行執(zhí)行;但某些任務依賴于其它某些任務,只能等那些任務完成后才能執(zhí)行。

雖然我們可以使用async.parallel和async.series結合起來實現(xiàn)該功能,但如果任務之間關系復雜,則代碼會相當復雜,以后如果想添加一個新任務,也會很麻煩。這時使用async.auto,則會事半功倍。

如果有任務中途出錯,則會把該錯誤傳給最終callback,所有任務(包括已經執(zhí)行完的)產生的數(shù)據(jù)將被忽略。

這里假設我要寫一個程序,它要完成以下幾件事:

從某處取得數(shù)據(jù)

在硬盤上建立一個新的目錄

將數(shù)據(jù)寫入到目錄下某文件

發(fā)送郵件,將文件以附件形式發(fā)送給其它人。

分析該任務,可以知道1與2可以并行執(zhí)行,3需要等1和2完成,4要等3完成。

async.auto({

getData: function (callback) {

setTimeout(function(){

console.log('1.1: got data');

callback();

}, 300);

},

makeFolder: function (callback) {

setTimeout(function(){

console.log('1.1: made folder');

callback();

}, 200);

},

writeFile: ['getData', 'makeFolder', function(callback) {

setTimeout(function(){

console.log('1.1: wrote file');

callback(null, 'myfile');

}, 300);

}],

emailFiles: ['writeFile', function(callback, results) {

log('1.1: emailed file: ', results.writeFile); // -> myfile

callback(null, results.writeFile);

}]

}, function(err, results) {

log('1.1: err: ', err); // -> null

log('1.1: results: ', results); // -> { makeFolder: undefined,

//? ? ? getData: undefined,

//? ? ? writeFile: 'myfile',

//? ? ? emailFiles: 'myfile' }

});

更多詳細示例參見:https://github.com/freewind/async_demo/blob/master/auto.js

5. whilst(test, fn, callback)(用可于異步調用的while)

相當于while,但其中的異步調用將在完成后才會進行下一次循環(huán)。舉例如下:

var count1 = 0;

async.whilst(

function() { return count1 < 3 },

function(cb) {

log('1.1 count: ', count1);

count1++;

setTimeout(cb, 1000);

},

function(err) {

// 3s have passed

log('1.1 err: ', err); // -> undefined

}

);

它相當于:

try {

whilst(test) {

fn();

}

callback();

} catch (err) {

callback(err);

}

該函數(shù)的功能比較簡單,條件變量通常定義在外面,可供每個函數(shù)訪問。在循環(huán)中,異步調用時產生的值實際上被丟棄了,因為最后那個callback只能傳入錯誤信息。

另外,第二個函數(shù)fn需要能接受一個函數(shù)cb,這個cb最終必須被執(zhí)行,用于表示出錯或正常結束。

更詳細示例參見:https://github.com/freewind/async_demo/blob/master/whilst_until.js

6. until(test, fn, callback) (與while相似,但判斷條件相反)

var count4 = 0;

async.until(

function() { return count4>3 },

function(cb) {

log('1.4 count: ', count4);

count4++;

setTimeout(cb, 200);

},

function(err) {

// 4s have passed

log('1.4 err: ',err); // -> undefined

}

);

當?shù)谝粋€函數(shù)條件為false時,繼續(xù)執(zhí)行第二個函數(shù),否則跳出。

7. queue (可設定worker數(shù)量的隊列)

queue相當于一個加強版的parallel,主要是限制了worker數(shù)量,不再一次性全部執(zhí)行。當worker數(shù)量不夠用時,新加入的任務將會排隊等候,直到有新的worker可用。

該函數(shù)有多個點可供回調,如worker用完時、無等候任務時、全部執(zhí)行完時等。

定義一個queue,其worker數(shù)量為2,并在任務執(zhí)行時,記錄一下日志:

var q =async.queue(function(task, callback) {

log('worker is processing task: ', task.name);

task.run(callback);

}, 2);

worker數(shù)量將用完時,會調用saturated函數(shù):

q.saturated = function() {

log('all workers to be used');

}

當最后一個任務交給worker執(zhí)行時,會調用empty函數(shù)

q.empty= function() {

log('no more tasks wating');

}

當所有任務都執(zhí)行完時,會調用drain函數(shù)

q.drain = function() {

console.log('all tasks have been processed');

}

放入多個任務,可一次放一個,或一次放多個

q.push({name:'t1',run: function(cb){

log('t1 is running, waiting tasks: ', q.length());

t.fire('t1', cb, 400); // 400ms后執(zhí)行

}}, function(err) {

log('t1 executed');

});

q.push([{name:'t3',run: function(cb){

log('t3 is running, waiting tasks: ', q.length());

t.fire('t3', cb, 300); // 300ms后執(zhí)行

}},{name:'t4',run: function(cb){

log('t4 is running, waiting tasks: ', q.length());

t.fire('t4', cb, 500); // 500ms后執(zhí)行

}}], function(err) {

log('t3/4 executed');

});

更多詳細示例參見:https://github.com/freewind/async_demo/blob/master/queue.js

8. iterator(tasks) (將幾個函數(shù)包裝為iterator)

將一組函數(shù)包裝成為一個iterator,可通過next()得到以下一個函數(shù)為起點的新的iterator。該函數(shù)通常由async在內部使用,但如果需要時,也可在我們的代碼中使用它。

var iter =async.iterator([

function() {console.log('111') },

function() {console.log('222') },

function() {console.log('333') }

]);

console.log(iter());

console.log(iter.next());

直接調用(),會執(zhí)行當前函數(shù),并返回一個由下個函數(shù)為起點的新的iterator。調用next(),不會執(zhí)行當前函數(shù),直接返回由下個函數(shù)為起點的新iterator。

對于同一個iterator,多次調用next(),不會影響自己。如果只剩下一個元素,調用next()會返回null。

更詳細示例參見:https://github.com/freewind/async_demo/blob/master/iterator.js

9. apply(function, arguments..) (給函數(shù)預綁定參數(shù))

apply是一個非常好用的函數(shù),可以讓我們給一個函數(shù)預綁定多個參數(shù)并生成一個可直接調用的新函數(shù),簡化代碼。

對于函數(shù):

function(callback) { t.inc(3, callback); }

可以用apply改寫為:

async.apply(t.inc, 3);

還可以給某些函數(shù)預設值,得到一個新函數(shù):

var log =async.apply(console.log, ">");

log('hello');

// > hello

更詳細代碼參見:https://github.com/freewind/async_demo/blob/master/apply.js

10. nextTick(callback) (在nodejs與瀏覽器兩邊行為一致)

nextTick的作用與nodejs的nextTick一樣,都是把某個函數(shù)調用放在隊列的尾部。但在瀏覽器端,只能使用setTimeout(callback,0),但這個方法有時候會讓其它高優(yōu)先級的任務插到前面去。

所以提供了這個nextTick,讓同樣的代碼在服務器端和瀏覽器端表現(xiàn)一致。

var calls = [];

async.nextTick(function() {

calls.push('two');

});

calls.push('one');

async.nextTick(function() {

console.log(calls); // -> [ 'one', 'two' ]

});

更詳細代碼參見:https://github.com/freewind/async_demo/blob/master/nextTick.js

二:工具類

Async中提供了幾個工具類,給我們提供一些小便利:

memoize

unmemoize

log

dir

noConflict

1. memoize(fn, [hasher])

有一些方法比較耗時,且對于相同的輸入總是有相同的輸出。這時可以使用memoize給它加個緩存,對于相同的參數(shù)只計算一次,以后就直接從緩存中取結果用了。

比如這里有一個很慢的函數(shù):

var slow_fn = function(x, y, callback) {

console.log(‘start working for: ‘ + x+’,'+y);

t.wait(100);

console.log(‘finished: ‘ + x+’,'+y);

callback(null, ‘im slow for: ‘+x+’,'+y);

};

可以用memoize生成一個新的帶緩存的函數(shù):

var fn = async.memoize(slow_fn);

試試同樣參數(shù)調用兩次:

fn(‘a’,'b’, function(err, result) {

console.log(result);

});

// 直接得到之前計算好的值

fn(‘a’,'b’, function(err, result) {

console.log(result);

});


注意memoize的參數(shù)中還有一個hasher,它是做什么用的呢?它可以讓我們自定義如果根據(jù)參數(shù)來判斷是否從緩存中取。默認情況下,兩次調用,只有參數(shù)完全一樣的時候才會從緩存中取。這里我們使用hasher來改變規(guī)則。

var fn_hasher = async.memoize(slow_fn, function(x,y) {

return x+y;

});


新定義的這個,將根據(jù)兩個參數(shù)的和來判斷。

fn_hasher(‘cd’,'e’, function(err, result) {

console.log(result);

});

fn_hasher(‘c’,'de’, function(err, result) {

console.log(result); // 可以取得前面(‘cd’,'e’)的計算結果

// im show for: cd,e

});


第二次的調用,雖然參數(shù)跟第一次不一樣,但是其和卻一樣,所以直接從緩存中拿到前次運行結果。

2. unmemoize(fn)

unmemoize的作用正好跟memoize相反,它可以把一個帶緩存的函數(shù)再變回原樣:

var fn2 = async.unmemoize(fn);

console.log(‘unmemoized’);

fn2(‘a’,'b’, function(err,result) {

console.log(result);

});


經過unmemoize后,再運行該函數(shù)就得重新運算了。

3. log(function, arguments)

log用于快速執(zhí)行某異步函數(shù),并記錄它的返回值。試驗函數(shù)時很方便,不用寫那些固定模式的代碼。

var x = function() {

this.name = ‘Freewind’;

}

var hello = function(name, callback) {

setTimeout(function() {

callback(null, ‘hello ‘ + name, ‘nice to see you ‘ + name, x, {a:’123′});

}, 200);

};

async.log(hello, ‘world’);

打印結果如下:

hello world

nice to see you world

[Function]

{ a: ’123′ }

可以看到,它直接運行了該函數(shù),并以每行一個參數(shù)的形式打印出了結果。

4. dir(function, arguments)

該函數(shù)與log非常像,不同之處在于,它最終調用了console.dir,而log最終調用了console.log。

看看使用dir打印的效果如何:

async.dir(hello, ‘world’);

結果:

‘hello world’

‘nice to see you world’

[Function]

{ a: ’123′ }

僅僅是多了幾個單引號。為了弄清楚dir存在的意義(什么情況下應該使用dir而不是log),我提了一個問題,參看:http://stackoverflow.com/questions/10636866/whats-the-difference-between-async-log-and-async-dir

5. noConflict

最后是這個noConflict,它僅僅用于瀏覽器端,在nodejs中沒用,這里無法演示。

它的作用是:如果之前已經在全局域中定義了async變量,當導入本async.js時,會先把之前的async變量保存起來,然后覆蓋它。用完之后,調用noConflict()方法,就會歸還該值。同時返回async本身供換名使用。

這里可以看一下它的實現(xiàn)代碼:

// global on the server, window in the browser

var root = this,

previous_async = root.async;

if (typeof module !== ‘undefined’ && module.exports) {

module.exports = async;

}

else {

root.async = async;

}

async.noConflict = function () {

root.async = previous_async;

return async;

};


可以看到,當處于nodejs或者commonjs環(huán)境中,它會執(zhí)行module.exports=async,在其它情況下(通常為瀏覽器端)才會root.async=async,將async賦值給root。

在瀏覽器中的用法如下:

// code using async

async.noConflict();

// Code that uses other library’s ‘async’ can follow here.

三:集合操作

Async提供了很多針對集合的函數(shù),可以簡化我們對集合進行異步操作時的步驟。如下:

forEach:對集合中每個元素進行異步操作

map:對集合中的每個元素通過異步操作得到另一個值,得到新的集合

filter:對集合中元素使用異步操作進行篩選,得到符合條件的集合

reject:與filter相似,只是判斷條件時正好相反,得到剩下的元素的集合

reduce:使用一個初始值同集合中每一個元素進行異步操作,最后得到一個唯一的結果

detect:得到集合中滿足條件的第一個數(shù)據(jù)

sortBy:對集合中的數(shù)據(jù)進行異步操作,再根據(jù)值從小到大排序

some/any:集合中是否有至少一個元素滿足條件

every/all:集合中是否每個元素都滿足條件

concat:對集合中的元素進行異步操作,將結果集合并成一個數(shù)組

下面一一解釋:

1. forEach(arr, iterator(item, callback), callback(err))

如果想對同一個集合中的所有元素都執(zhí)行同一個異步操作,可以利用forEach函數(shù)。注意該函數(shù)將重點放在“執(zhí)行過程”上,忽略運行后產生的數(shù)據(jù)。如果需要結果,可使用map函數(shù)。

根據(jù)執(zhí)行的方式不同,forEach提供了三個版本:

集合中所有元素并行執(zhí)行

一個一個順序執(zhí)行

分批執(zhí)行,同一批內并行,批與批之間按順序

首先看并行執(zhí)行的例子,它比較簡單,只是打印出傳入的元素內容:

var arr = [{name:'Jack', delay: 200},

{name:'Mike', delay: 100},

{name:'Freewind', delay: 300}];


async.forEach(arr, function(item, callback) {

log(’1.1 enter: ‘ + item.name);

setTimeout(function(){

log(’1.1 handle: ‘ + item.name);

callback();

}, item.delay);

}, function(err) {

log(’1.1 err: ‘ + err);

});


它將打出如下結果:

42.244> 1.1 enter: Jack

42.245> 1.1 enter: Mike

42.245> 1.1 enter: Freewind

42.350> 1.1 handle: Mike

42.445> 1.1 handle: Jack

42.554> 1.1 handle: Freewind

42.554> 1.1 err: undefined

最前面的數(shù)據(jù)是當前的時間值(秒.毫秒),從中可以看到各異步操作是并行執(zhí)行的。

如果想同步執(zhí)行,需要使用forEachSeries函數(shù),它與forEach的用法一模一樣,只是執(zhí)行時是一個一個來的。這里就不給例子了。

當集合中元素很多,既不想一次全部并行操作,又不想一個一個按順序來,可以使用forEachLimit函數(shù)。它可以設定一批處理幾個,每一批內并行執(zhí)行,批與批之間順序執(zhí)行。

async.forEachLimit(arr, 2, function(item, callback) {

log(’1.5 enter: ‘ + item.name);

setTimeout(function(){

log(’1.5 handle: ‘ + item.name);

callback(null, item.name);

}, item.delay);

}, function(err) {

log(’1.5 err: ‘ + err);

});


打印結果如下:

42.247> 1.5 enter: Jack

42.248> 1.5 enter: Mike

42.351> 1.5 handle: Mike

42.352> 1.5 enter: Freewind

42.461> 1.5 handle: Jack

42.664> 1.5 handle: Freewind

42.664> 1.5 err: undefined

可以看到前兩個是同時開始的,而第三個是等前兩個都完成以后才開始的。

更多詳細示例:https://github.com/freewind/async_demo/blob/master/forEach.js

2. map(arr, iterator(item, callback), callback(err, results))

map的重點是轉換,即把集合中的元素通過異步操作轉為另一個對象,最后可以得到轉換后的對象數(shù)組。它也提供了并行與順序執(zhí)行兩種方式。

這里給一個示例,給集合中的每個元素以異步方式增加!!!:

var arr = [{name:'Jack', delay:200}, {name:'Mike', delay: 100}, {name:'Freewind', delay:300}, {name:'Test', delay: 50}];

async.map(arr, function(item, callback) {

log(’1.1 enter: ‘ + item.name);

setTimeout(function() {

log(’1.1 handle: ‘ + item.name);

callback(null, item.name+’!!!’);

}, item.delay);

}, function(err,results) {

log(’1.1 err: ‘, err);

log(’1.1 results: ‘, results);

});


打印結果如下:

54.569> 1.1 enter: Jack

54.569> 1.1 enter: Mike

54.569> 1.1 enter: Freewind

54.569> 1.1 enter: Test

54.629> 1.1 handle: Test

54.679> 1.1 handle: Mike

54.789> 1.1 handle: Jack

54.879> 1.1 handle: Freewind

54.879> 1.1 err:

54.879> 1.1 results: [ 'Jack!!!', 'Mike!!!', 'Freewind!!!', 'Test!!!' ]

可以看到,對各元素的操作是并行的,結果會匯總在一起交給最后的回調。

如果想順序執(zhí)行,可使用mapSeries,它與map的用法一模一樣。

更多詳細示例:https://github.com/freewind/async_demo/blob/master/map.js

3. filter(arr, iterator(item, callback(test)), callback(results))

使用異步操作對集合中的元素進行篩選。需要注意的是,iterator的callback只有一個參數(shù),只能接收true或false。

對于出錯,該函數(shù)沒有做出任何處理,直接由nodejs拋出。所以需要注意對Error的處理。

提供了并行與順序執(zhí)行兩種方式。

并行示例,找到所有>=3的元素:

async.filter([1,2,3,4,5], function(item, callback) {

log(’1.1 enter: ‘ + item);

setTimeout(function() {

log(’1.1 test: ‘ + item);

callback(item>=3);

}, 200);

}, function(results) {

log(’1.1 results: ‘, results);

});


打印結果如下:

16.739> 1.1 enter: 1

16.749> 1.1 enter: 2

16.749> 1.1 enter: 3

16.749> 1.1 enter: 4

16.749> 1.1 enter: 5

16.749> 1.3 enter: 1

16.949> 1.1 test: 1

16.949> 1.1 test: 2

16.949> 1.1 test: 3

16.949> 1.1 test: 4

16.949> 1.1 test: 5

16.949> 1.1 results: [ 3, 4, 5 ]

可見找到了滿足條件的所有元素。

如果需要順序執(zhí)行,可以使用filterSeries函數(shù),它的用法與filter一樣。

更多詳細示例:https://github.com/freewind/async_demo/blob/master/filter_reject.js

4. reject(arr, iterator(item, callback(test)), callback(results))

reject與filter相似,只是行為正好相反。當條件為true時,它將丟棄相應的元素。它也提供了并行與順序執(zhí)行兩種方式。

并行示例,去掉所有>=3的元素:

async.reject([1,2,3,4,5], function(item, callback) {

log(’1.4 enter: ‘ + item);

setTimeout(function() {

log(’1.4 test: ‘ + item);

callback(item>=3);

}, 200);

}, function(results) {

log(’1.4 results: ‘, results);

});

打印結果如下:

31.359> 1.4 enter: 1

31.359> 1.4 enter: 2

31.359> 1.4 enter: 3

31.359> 1.4 enter: 4

31.359> 1.4 enter: 5

31.559> 1.4 test: 1

31.559> 1.4 test: 2

31.559> 1.4 test: 3

31.559> 1.4 test: 4

31.559> 1.4 test: 5

31.569> 1.4 results: [ 1, 2 ]

如果想順序執(zhí)行,可使用rejectSeries,它與reject用法一樣。

更多詳細示例:https://github.com/freewind/async_demo/blob/master/filter_reject.js

5. reduce(arr, memo, iterator(memo,item,callback), callback(err,result))

Reduce可以讓我們給定一個初始值,用它與集合中的每一個元素做運算,最后得到一個值。reduce從左向右來遍歷元素,如果想從右向左,可使用reduceRight。

這里給個例子,計算出100與某個集合中所有數(shù)之和:

var arr = [1,3,5];

async.reduce(arr, 100, function(memo, item, callback) {

log(’1.1 enter: ‘ + memo +’, ‘ + item);

setTimeout(function() {

callback(null, memo+item);

}, 100);

},function(err, result) {

log(’1.1 err: ‘, err);

log(’1.1 result: ‘, result);

});


將打印出結果:

28.789> 1.1 enter: 100, 1

28.889> 1.1 enter: 101, 3

28.999> 1.1 enter: 104, 5

29.109> 1.1 err:

29.109> 1.1 result: 109

需要注意的是,async中的reduce,不是并行操作,而是對元素一個個順序操作,所以當元素比較多時,性能會比較弱。如果想提高性能,可使用async.map函數(shù),先并行得到集合中每個元素被處理之后的值,然后再使用Array.prototype.reduce函數(shù)處理,性能會快很多。

對于這個例子:

async.reduce(arr, 100, function(memo,item,callback) {

log(’1.4 enter: ‘+memo+’,'+item);

t.inc(item, function(err,n) {

log(’1.4 handle: ‘,n);

callback(null, memo+n);

});

}, function(err,result) {

log(’1.4 err: ‘, err);

log(’1.4 result: ‘, result);

});


它總耗時為0.62秒。如果換成map+array.reduce:

async.map(arr, function(item, callback) {

log(’1.5 enter: ‘, item);

t.inc(item, function(err,n){

log(’1.5 handle: ‘, n);

callback(null,n);

});

},function(err, results) {

log(’1.5 err: ‘, err);

log(’1.5 results: ‘, results);

var sum = results.reduce(function(memo, item) {

return memo + item;

}, 100);

log(’1.5 sum: ‘, sum);

});


耗時為0.21秒。

更多詳細示例:https://github.com/freewind/async_demo/blob/master/reduce.js

6. detect(array, iterator(item,callback(test)), callback(result)

用于取得集合中滿足條件的第一個元素。它分為并行與順序執(zhí)行兩種方式,分別對應函數(shù)detect和detectSeries。

并行示例,找到一個奇數(shù):

var arr = [{value:1,delay:500},

{value:2,delay:200},

{value:3,delay:300}];

async.detect(arr, function(item,callback){

log(’1.1 enter: ‘, item.value);

setTimeout(function() {

log(’1.1 handle: ‘, item.value);

callback(n%2===1);

}, item.delay);

}, function(result) {

log(’1.1 result: ‘, result);

});


結果如下:

09.928> 1.1 enter: 1

09.928> 1.1 enter: 2

09.928> 1.1 enter: 3

10.138> 1.1 handle: 2

10.228> 1.1 handle: 3

10.228> 1.1 result: { value: 3, delay: 300 }

10.438> 1.1 handle: 1

10.438> 1.1 handle: 1

可見得到了最先執(zhí)行完的那個奇數(shù)3.

更多詳細示例:https://github.com/freewind/async_demo/blob/master/detect.js

7. sortBy(array, iterator(item,callback(err,result)), callback(err,results))

對集合內的元素進行排序,依據(jù)每個元素進行某異步操作后產生的值,從小到大排序。

示例:

var arr = [3,6,1];

async.sortBy(arr, function(item, callback) {

setTimeout(function() {

callback(null,item);

}, 200);

}, function(err,results) {

log(’1.1 err: ‘, err);

log(’1.1 results: ‘, results);

});


打印結果如下:

26.562> 1.1 err: null

26.562> 1.1 results: [ 1, 3, 6 ]

可以看到集合中的數(shù)據(jù)從小到大排好了序。

更多詳細示例:https://github.com/freewind/async_demo/blob/master/sortBy.js

8. some/any(arr, iterator(item,callback(test)), callback(result))

當集合中是否有至少一個元素滿足條件時,最終callback得到的值為true,否則為false。它有一個別名叫any。

判斷集合中是否有元素小于等于3:

async.some([1,2,3,6], function(item,callback){

log(’1.1 enter: ‘,item);

setTimeout(function(){

log(’1.1 handle: ‘,item);

callback(item<=3);

},100);

}, function(result) {

log(’1.1 result: ‘, result);

});

打印結果如下:

36.165> 1.1 enter: 1

36.165> 1.1 enter: 2

36.165> 1.1 enter: 3

36.165> 1.1 enter: 6

36.275> 1.1 handle: 1

36.275> 1.1 result: true

36.275> 1.1 handle: 2

36.275> 1.1 handle: 3

36.275> 1.1 handle: 6


可見的確得到了結果true。

更多詳細示例:https://github.com/freewind/async_demo/blob/master/some.js

9. every/all(arr, iterator(item,callback), callback(result))

如果集合里每一個元素都滿足條件,則傳給最終回調的result為true,否則為false

在下面的示例中,因為集合中每個元素都<=10,所以最終結果為true

async.every(arr, function(item,callback){

log(’1.1 enter: ‘,item);

setTimeout(function(){

log(’1.1 handle: ‘,item);

callback(item<=10);

},100);

}, function(result) {

log(’1.1 result: ‘, result);

});


打印如下:

32.113> 1.1 enter: 1

32.123> 1.1 enter: 2

32.123> 1.1 enter: 3

32.123> 1.1 enter: 6

32.233> 1.1 handle: 1

32.233> 1.1 handle: 2

32.233> 1.1 handle: 3

32.233> 1.1 handle: 6

32.233> 1.1 result: true

可見最終結果為true

更多詳細示例:https://github.com/freewind/async_demo/blob/master/every.js

10. concat(arr, iterator(item,callback(err,result)), callback(err,result))

將合并多個異步操作的結果合并為一個數(shù)組。

在下面的示例中,將集合中的每一個元素都加倍:

async.concat(['aa','bb'], function(item,callback) {

??? setTimeout(function() {

??????? callback(null, [item, item]);

??? }, 100);

}, function(err, values) {

??? log(’1.1 err: ‘, err);

??? log(’1.1 values: ‘, values);

});

打印如下:

13.539> 1.1 err:

13.639> 1.1 values: [ 'aa', 'aa', 'bb', 'bb' ]

打印出來的是經過合并后的數(shù)組。

更多詳細示例:https://github.com/freewind/async_demo/blob/master/concat.js

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容