├── README.md ├── apply.js ├── auto.js ├── concat.js ├── detect.js ├── every.js ├── filter_reject.js ├── forEach.js ├── iterator.js ├── map.js ├── nextTick.js ├── parallel.js ├── queue.js ├── reduce.js ├── series.js ├── some.js ├── sortBy.js ├── t.js ├── utils.js ├── waterfall.js └── whilst_until.js /README.md: -------------------------------------------------------------------------------- 1 | async_demo 2 | ========== 3 | 4 | A demo for async 5 | 6 | 因为我已很久未使用nodejs了,例子中的错误无法修正,推荐大家看这个:https://github.com/alsotang/async_demo -------------------------------------------------------------------------------- /apply.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | var t = require('./t'); 4 | var log = t.log; 5 | 6 | /** 7 | * async.apply是一个非常好用的函数,可以让我们给一个函数预绑定多个参数并生成一个可直接调用的新函数,简化代码。 8 | * 9 | * function(callback) { t.inc(3, callback); } 10 | * 等价于: 11 | * async.apply(t.inc, 3); 12 | */ 13 | // apply(function, arguments..) 14 | 15 | async.parallel([ 16 | async.apply(t.inc, 3), 17 | async.apply(t.fire, 100) 18 | ], function (err, results) { 19 | log('err: ', err); 20 | log('results: ', results); 21 | }); 22 | 23 | // 预设参数 24 | var x = async.apply(t.inc, 1); 25 | x(function(err, n){ 26 | console.log('1.inc: ' + n); 27 | }); 28 | -------------------------------------------------------------------------------- /auto.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | var t = require('./t'); 4 | var log = t.log; 5 | 6 | /** 7 | * async.auto用来处理有依赖关系的多个任务的执行。比如某些任务之间彼此独立,可以并行执行;但某些任务依赖于其它某些任务,只能等那些任务完成后才能执行。 8 | * 9 | * 虽然我们可以使用async.parallel和async.series结合起来实现该功能,但如果任务之间关系复杂,则代码会相当复杂,以后如果想添加一个新任务,也会很麻烦。 10 | * 11 | * 这时使用async.auto,则会事半功倍。 12 | * 13 | * 如果有任务中途出错,则会把该错误传给最终callback,所有任务(包括已经执行完的)产生的数据将被忽略。 14 | * 15 | * 如果不关心错误和最终数据,可以不用写最后那个callback。 16 | */ 17 | // async.auto(tasks, [callback]) 18 | 19 | /** 20 | * 我要写一个程序,它要完成以下几件事: 21 | * 1. 从某处取得数据 22 | * 2. 在硬盘上建立一个新的目录 23 | * 3. 将数据写入到目录下某文件 24 | * 4. 发送邮件,将文件以附件形式发送给其它人。 25 | * 26 | * 分析该任务,可以知道1与2可以并行执行,3需要等1和2完成,4要等3完成。 27 | * 28 | * 可以按以下方式来使用auto函数。 29 | */ 30 | // 1.1 31 | async.auto({ 32 | getData: function (callback) { 33 | setTimeout(function(){ 34 | console.log('1.1: got data'); 35 | callback(); 36 | }, 300); 37 | }, 38 | makeFolder: function (callback) { 39 | setTimeout(function(){ 40 | console.log('1.1: made folder'); 41 | callback(); 42 | }, 200); 43 | }, 44 | writeFile: ['getData', 'makeFolder', function(callback) { 45 | setTimeout(function(){ 46 | console.log('1.1: wrote file'); 47 | callback(null, 'myfile'); 48 | }, 300); 49 | }], 50 | emailFiles: ['writeFile', function(callback, results) { 51 | log('1.1: emailed file: ', results.writeFile); // -> myfile 52 | callback(null, results.writeFile); 53 | }] 54 | }, function(err, results) { 55 | log('1.1: err: ', err); // -> null 56 | log('1.1: results: ', results); // -> { makeFolder: undefined, 57 | // getData: undefined, 58 | // writeFile: 'myfile', 59 | // emailFiles: 'myfile' } 60 | }); 61 | 62 | /** 63 | * 如果中途出错,则会把错误交给最终callback,所有任务(包括执行完的和未执行完的)产生的数据都被忽略。 64 | */ 65 | // 1.2 66 | async.auto({ 67 | getData: function (callback) { 68 | setTimeout(function(){ 69 | console.log('1.2: got data'); 70 | callback(null, 'mydata'); 71 | }, 300); 72 | }, 73 | makeFolder: function (callback) { 74 | setTimeout(function(){ 75 | console.log('1.2: made folder'); 76 | callback(null, 'myfolder'); 77 | }, 200); 78 | }, 79 | writeFile: ['getData', 'makeFolder', function(callback, results) { 80 | setTimeout(function(){ 81 | console.log('1.2: wrote file'); 82 | callback('myerr'); 83 | }, 300); 84 | }], 85 | emailFiles: ['writeFile', function(callback, results) { 86 | console.log('1.2: emailed file: ' + results.writeFile); 87 | callback('err sending email', results.writeFile); 88 | }] 89 | }, function(err, results) { 90 | log('1.2 err: ', err); // -> myerr 91 | log('1.2 results: ', results); // -> '' 92 | }); -------------------------------------------------------------------------------- /concat.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | var t = require('./t'); 4 | var log = t.log; 5 | 6 | /** 7 | * 将合并多个异步操作的结果合并为一个数组。 8 | */ 9 | // concat(arr, iterator(item,callback(err,result)), callback(err,result)) 10 | 11 | var data = { 12 | aaa: [11,22,33], 13 | bbb: [44,55], 14 | ccc: 66 15 | }; 16 | 17 | var keys = [ 18 | {name: 'aaa', delay: 300}, 19 | {name: 'bbb', delay: 100}, 20 | {name: 'ccc', delay: 200} 21 | ]; 22 | 23 | /** 24 | * 以并行方式对集合中各元素进行异步操作,然后把得到的结果合并为一个数组,传给最后的callback。 25 | */ 26 | // 1.1 27 | async.concat(keys, function(key,callback) { 28 | setTimeout(function() { 29 | callback(null, data[key.name]); 30 | }, key.delay); 31 | }, function(err, values) { 32 | log('1.1 err: ', err); 33 | log('1.1 values: ', values); 34 | }); 35 | // 13.539> 1.1 err: 36 | // 13.539> 1.1 values: [ 44, 55, 66, 11, 22, 33 ] 37 | 38 | /** 39 | * 如果中途出错,则把错误以及已经完成的操作的结果交给最后callback。未执行完的则忽略。 40 | */ 41 | // 1.2 42 | async.concat(keys, function(key,callback) { 43 | setTimeout(function() { 44 | if(key.name==='ccc') callback('myerr'); 45 | else callback(null, data[key.name]); 46 | }, key.delay); 47 | }, function(err, values) { 48 | log('1.2 err: ', err); 49 | log('1.2 values: ', values); 50 | }); 51 | // 13.439> 1.2 err: myerr 52 | // 13.439> 1.2 values: [ 44, 55 ] 53 | 54 | /** 55 | * 按数组中的元素顺序来执行异步操作,一个完成后才对下一个进行操作。所有结果会汇集成一个数组交给最后的callback。 56 | */ 57 | // concatSeries(arr, iterator, callback) 58 | 59 | // 1.3 60 | async.concatSeries(keys, function(key,callback) { 61 | setTimeout(function() { 62 | callback(null, data[key.name]); 63 | }, key.delay); 64 | }, function(err, values) { 65 | log('1.3 err: ', err); 66 | log('1.3 values: ', values); 67 | }); 68 | // 13.859> 1.3 err: 69 | // 13.859> 1.3 values: [ 11, 22, 33, 44, 55, 66 ] -------------------------------------------------------------------------------- /detect.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freewind/async_demo/b084ebd25d7836a36ef2f3f34e54b3c0634bf689/detect.js -------------------------------------------------------------------------------- /every.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freewind/async_demo/b084ebd25d7836a36ef2f3f34e54b3c0634bf689/every.js -------------------------------------------------------------------------------- /filter_reject.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | var t = require('./t'); 4 | var log = t.log; 5 | 6 | /** 7 | * 使用异步操作对集合中的元素进行筛选。需要注意的是,iterator的callback只有一个参数,只能接收true或false。 8 | * 9 | * 对于出错,该函数没有做出任何处理,直接由nodejs抛出。所以需要注意对Error的处理。 10 | * 11 | * async提供了两种方式: 12 | * 1. 并行执行:filter 13 | * 2. 顺序执行:filterSereis 14 | */ 15 | // filter(arr, iterator(item, callback(test)), callback(results)) 16 | 17 | var arr = [1,2,3,4,5]; 18 | 19 | /** 20 | * 并行执行,对arr进行筛选。 21 | */ 22 | async.filter(arr, function(item, callback) { 23 | log('1.1 enter: ' + item); 24 | setTimeout(function() { 25 | log('1.1 test: ' + item); 26 | callback(item>=3); 27 | }, 200); 28 | }, function(results) { 29 | log('1.1 results: ', results); 30 | }); 31 | // 16.739> 1.1 enter: 1 32 | // 16.749> 1.1 enter: 2 33 | // 16.749> 1.1 enter: 3 34 | // 16.749> 1.1 enter: 4 35 | // 16.749> 1.1 enter: 5 36 | // 16.749> 1.3 enter: 1 37 | // 16.949> 1.1 test: 1 38 | // 16.949> 1.1 test: 2 39 | // 16.949> 1.1 test: 3 40 | // 16.949> 1.1 test: 4 41 | // 16.949> 1.1 test: 5 42 | // 16.949> 1.1 results: [ 3, 4, 5 ] 43 | 44 | 45 | /** 46 | * 如果出错,将会由nodejs抛出,导致出错。为保证其它代码正常运行,注释掉该测试。 47 | */ 48 | /* 49 | async.filter(arr, function(item, callback) { 50 | log('1.2 enter: ' + item); 51 | setTimeout(function() { 52 | log('1.2 handle: ' + item); 53 | if(item===2) { 54 | throw new Error('myerr'); 55 | } 56 | callback(item>=3); 57 | }, 100); 58 | }, function(results) { 59 | log('1.2 results: ', results); 60 | }); 61 | */ 62 | 63 | /** 64 | * 并行执行,对arr进行筛选。 65 | */ 66 | // 1.3 67 | async.filterSeries(arr, function(item, callback) { 68 | log('1.3 enter: ' + item); 69 | setTimeout(function() { 70 | log('1.3 handle: ' + item); 71 | callback(item>=3); 72 | }, 200); 73 | }, function(results) { 74 | log('1.3 results: ', results); 75 | }); 76 | // 16.749> 1.3 enter: 1 77 | // 16.949> 1.3 handle: 1 78 | // 16.949> 1.3 enter: 2 79 | // 17.149> 1.3 handle: 2 80 | // 17.149> 1.3 enter: 3 81 | // 17.369> 1.3 handle: 3 82 | // 17.369> 1.3 enter: 4 83 | // 17.589> 1.3 handle: 4 84 | // 17.589> 1.3 enter: 5 85 | // 17.789> 1.3 handle: 5 86 | // 17.789> 1.3 results: [ 3, 4, 5 ] 87 | 88 | 89 | /* 90 | * reject跟filter正好相反,当测试为true时,抛弃之 91 | */ 92 | // reject(arr, iterator(item, callback(test)), callback(results) 93 | async.reject(arr, function(item, callback) { 94 | log('1.4 enter: ' + item); 95 | setTimeout(function() { 96 | log('1.4 test: ' + item); 97 | callback(item>=3); 98 | }, 200); 99 | }, function(results) { 100 | log('1.4 results: ', results); 101 | }); 102 | // 31.359> 1.4 enter: 1 103 | // 31.359> 1.4 enter: 2 104 | // 31.359> 1.4 enter: 3 105 | // 31.359> 1.4 enter: 4 106 | // 31.359> 1.4 enter: 5 107 | // 31.559> 1.4 test: 1 108 | // 31.559> 1.4 test: 2 109 | // 31.559> 1.4 test: 3 110 | // 31.559> 1.4 test: 4 111 | // 31.559> 1.4 test: 5 112 | // 31.569> 1.4 results: [ 1, 2 ] -------------------------------------------------------------------------------- /forEach.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | var t = require('./t'); 4 | var log = t.log; 5 | 6 | /** 7 | * 如果想对同一个集合中的所有元素都执行同一个异步操作,可以利用forEach函数。 8 | * 9 | * async提供了三种方式: 10 | * 1. 集合中所有元素并行执行 11 | * 2. 一个一个顺序执行 12 | * 3. 分批执行,同一批内并行,批与批之间按顺序 13 | * 14 | * 如果中途出错,则错误将上传给最终的callback处理。其它已经启动的任务继续执行,未启动的忽略。 15 | */ 16 | // forEach(arr, iterator(item, callback), callback(err)) 17 | 18 | 19 | var arr = [{name:'Jack', delay: 200}, 20 | {name:'Mike', delay: 100}, 21 | {name:'Freewind', delay: 300}]; 22 | 23 | /** 24 | * 所有操作并发执行,且全部未出错,最终得到的err为undefined。注意最终callback只有一个参数err。 25 | */ 26 | // 1.1 27 | async.forEach(arr, function(item, callback) { 28 | log('1.1 enter: ' + item.name); 29 | setTimeout(function(){ 30 | log('1.1 handle: ' + item.name); 31 | callback(null, item.name); 32 | }, item.delay); 33 | }, function(err) { 34 | log('1.1 err: ' + err); 35 | }); 36 | // 输出如下: 37 | // 42.244> 1.1 enter: Jack 38 | // 42.245> 1.1 enter: Mike 39 | // 42.245> 1.1 enter: Freewind 40 | // 42.350> 1.1 handle: Mike 41 | // 42.445> 1.1 handle: Jack 42 | // 42.554> 1.1 handle: Freewind 43 | // 42.554> 1.1 err: undefined 44 | 45 | 46 | /** 47 | * 如果中途出错,则出错后马上调用最终的callback。其它未执行完的任务继续执行。 48 | */ 49 | async.forEach(arr,function(item, callback) { 50 | log('1.2 enter: ' +item.name); 51 | setTimeout(function() { 52 | log('1.2 handle: ' + item.name); 53 | if(item.name==='Jack') { 54 | callback('myerr'); 55 | } 56 | }, item.delay); 57 | }, function(err) { 58 | log('1.2 err: ' + err); 59 | }); 60 | // 输出如下: 61 | // 42.246> 1.2 enter: Jack 62 | // 42.246> 1.2 enter: Mike 63 | // 42.246> 1.2 enter: Freewind 64 | // 42.350> 1.2 handle: Mike 65 | // 42.445> 1.2 handle: Jack 66 | // 42.446> 1.2 err: myerr 67 | // 42.555> 1.2 handle: Freewind 68 | 69 | /** 70 | * 与forEach相似,但不是并行执行。而是一个个按顺序执行。 71 | */ 72 | async.forEachSeries(arr, function(item, callback) { 73 | log('1.3 enter: ' + item.name); 74 | setTimeout(function(){ 75 | log('1.3 handle: ' + item.name); 76 | callback(null, item.name); 77 | }, item.delay); 78 | }, function(err) { 79 | log('1.3 err: ' + err); 80 | }); 81 | // 42.247> 1.3 enter: Jack 82 | // 42.459> 1.3 handle: Jack 83 | // 42.459> 1.3 enter: Mike 84 | // 42.569> 1.3 handle: Mike 85 | // 42.569> 1.3 enter: Freewind 86 | // 42.883> 1.3 handle: Freewind 87 | // 42.883> 1.3 err: undefined 88 | 89 | /** 90 | * 如果中途出错,则马上把错误传给最终的callback,还未执行的不再执行。 91 | */ 92 | async.forEachSeries(arr,function(item, callback) { 93 | log('1.4 enter: ' +item.name); 94 | setTimeout(function() { 95 | log('1.4 handle: ' + item.name); 96 | if(item.name==='Jack') { 97 | callback('myerr'); 98 | } 99 | }, item.delay); 100 | }, function(err) { 101 | log('1.4 err: ' + err); 102 | }); 103 | // 42.247> 1.4 enter: Jack 104 | // 42.460> 1.4 handle: Jack 105 | // 42.460> 1.4 err: myerr 106 | 107 | /** 108 | * 分批执行,第二个参数是每一批的个数。每一批内并行执行,但批与批之间按顺序执行。 109 | */ 110 | async.forEachLimit(arr, 2, function(item, callback) { 111 | log('1.5 enter: ' + item.name); 112 | setTimeout(function(){ 113 | log('1.5 handle: ' + item.name); 114 | callback(null, item.name); 115 | }, item.delay); 116 | }, function(err) { 117 | log('1.5 err: ' + err); 118 | }); 119 | // 42.247> 1.5 enter: Jack 120 | // 42.248> 1.5 enter: Mike 121 | // 42.351> 1.5 handle: Mike 122 | // 42.352> 1.5 enter: Freewind 123 | // 42.461> 1.5 handle: Jack 124 | // 42.664> 1.5 handle: Freewind 125 | // 42.664> 1.5 err: undefined 126 | 127 | /** 128 | * 如果中途出错,错误将马上传给最终的callback。同一批中的未执行完的任务还将继续执行,但下一批及以后的不再执行。 129 | */ 130 | async.forEachLimit(arr,2,function(item, callback) { 131 | log('1.6 enter: ' +item.name); 132 | setTimeout(function() { 133 | log('1.6 handle: ' + item.name); 134 | if(item.name==='Jack') { 135 | callback('myerr'); 136 | } 137 | }, item.delay); 138 | }, function(err) { 139 | log('1.6 err: ' + err); 140 | }); 141 | // 42.248> 1.6 enter: Jack 142 | // 42.248> 1.6 enter: Mike 143 | // 42.352> 1.6 handle: Mike 144 | // 42.462> 1.6 handle: Jack 145 | // 42.462> 1.6 err: myerr -------------------------------------------------------------------------------- /iterator.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | var t = require('./t'); 4 | var log = t.log; 5 | 6 | /** 7 | * 将一组函数包装成为一个iterator,可通过next()得到以下一个函数为起点的新的iterator。该函数通常由async在内部使用,但如果需要时,也可在我们的代码中使用它。 8 | */ 9 | // async.iterator(tasks) 10 | 11 | var iter = async.iterator([ 12 | function() { console.log('111') }, 13 | function() { console.log('222') }, 14 | function() { console.log('333') } 15 | ]); 16 | 17 | // 直接调用(),会执行当前函数,并返回一个由下个函数为起点的新的iterator 18 | console.log('------- iter() ---------'); 19 | console.log(iter()); 20 | console.log(iter()); 21 | 22 | // 调用next(),不会执行当前函数,直接返回由下个函数为起点的新iterator 23 | console.log('-------- iter.next() -------'); 24 | console.log(iter.next()); 25 | console.log(iter.next()); 26 | console.log(iter.next()); 27 | console.log(iter.next()); 28 | 29 | // 对于同一个iterator,多次调用next(),不会影响自己 30 | console.log('-------- iter.next()() -------'); 31 | console.log(iter.next()()); 32 | console.log(iter.next()()); 33 | 34 | // 如果只有一个元素,则next()返回null 35 | console.log('--------- last.next() --------'); 36 | console.log(iter.next().next().next()); -------------------------------------------------------------------------------- /map.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | var t = require('./t'); 4 | var log = t.log; 5 | 6 | /** 7 | * 对集合中的每一个元素,执行某个异步操作,得到结果。所有的结果将汇总到最终的callback里。与forEach的区别是,forEach只关心操作不管最后的值,而map关心的最后产生的值。 8 | * 9 | * 提供了两种方式: 10 | * 1. 并行执行。同时对集合中所有元素进行操作,结果汇总到最终callback里。如果出错,则立刻返回错误以及已经执行完的任务的结果,未执行完的占个空位 11 | * 2. 顺序执行。对集合中的元素一个一个执行操作,结果汇总到最终callback里。如果出错,则立刻返回错误以及已经执行完的结果,未执行的被忽略。 12 | */ 13 | // map(arr, iterator(item, callback), callback(err, results)) 14 | 15 | var arr = [{name:'Jack', delay:200}, {name:'Mike', delay: 100}, {name:'Freewind', delay:300}, {name:'Test', delay: 50}]; 16 | 17 | /** 18 | * 所有操作均正确执行,未出错。所有结果按元素顺序汇总给最终的callback。 19 | */ 20 | // 1.1 21 | async.map(arr, function(item, callback) { 22 | log('1.1 enter: ' + item.name); 23 | setTimeout(function() { 24 | log('1.1 handle: ' + item.name); 25 | callback(null, item.name + '!!!'); 26 | }, item.delay); 27 | }, function(err,results) { 28 | log('1.1 err: ', err); 29 | log('1.1 results: ', results); 30 | }); 31 | // 54.569> 1.1 enter: Jack 32 | // 54.569> 1.1 enter: Mike 33 | // 54.569> 1.1 enter: Freewind 34 | // 54.569> 1.1 enter: Test 35 | // 54.629> 1.1 handle: Test 36 | // 54.679> 1.1 handle: Mike 37 | // 54.789> 1.1 handle: Jack 38 | // 54.879> 1.1 handle: Freewind 39 | // 54.879> 1.1 err: 40 | // 54.879> 1.1 results: [ 'Jack!!!', 'Mike!!!', 'Freewind!!!', 'Test!!!' ] 41 | 42 | /** 43 | * 如果中途出错,立刻将错误、以及已经执行完成的结果汇总给最终callback。未执行完的将会在结果数组中用占个空位。 44 | */ 45 | async.map(arr, function(item, callback) { 46 | log('1.2 enter: ' + item.name); 47 | setTimeout(function() { 48 | log('1.2 handle: ' + item.name); 49 | if(item.name==='Jack') callback('myerr'); 50 | else callback(null, item.name+'!!!'); 51 | }, item.delay); 52 | }, function(err, results) { 53 | log('1.2 err: ', err); 54 | log('1.2 results: ', results); 55 | }); 56 | // 54.569> 1.2 enter: Jack 57 | // 54.569> 1.2 enter: Mike 58 | // 54.569> 1.2 enter: Freewind 59 | // 54.569> 1.2 enter: Test 60 | // 54.629> 1.2 handle: Test 61 | // 54.679> 1.2 handle: Mike 62 | // 54.789> 1.2 handle: Jack 63 | // 54.789> 1.2 err: myerr 64 | // 54.789> 1.2 results: [ undefined, 'Mike!!!', , 'Test!!!' ] 65 | // 54.879> 1.2 handle: Freewind 66 | 67 | /** 68 | * 顺序执行,一个完了才执行下一个。 69 | */ 70 | async.mapSeries(arr, function(item, callback) { 71 | log('1.3 enter: ' + item.name); 72 | setTimeout(function() { 73 | log('1.3 handle: ' + item.name); 74 | callback(null, item.name+'!!!'); 75 | }, item.delay); 76 | }, function(err,results) { 77 | log('1.3 err: ', err); 78 | log('1.3 results: ', results); 79 | }); 80 | // 54.569> 1.3 enter: Jack 81 | // 54.789> 1.3 handle: Jack 82 | // 54.789> 1.3 enter: Mike 83 | // 54.899> 1.3 handle: Mike 84 | // 54.899> 1.3 enter: Freewind 85 | // 55.209> 1.3 handle: Freewind 86 | // 55.209> 1.3 enter: Test 87 | // 55.269> 1.3 handle: Test 88 | // 55.269> 1.3 err: 89 | // 55.269> 1.3 results: [ 'Jack!!!', 'Mike!!!', 'Freewind!!!', 'Test!!!' ] 90 | 91 | /** 92 | * 顺序执行过程中出错,只把错误以及执行完的传给最终callback,未执行的忽略。 93 | */ 94 | async.mapSeries(arr, function(item, callback) { 95 | log('1.4 enter: ' + item.name); 96 | setTimeout(function() { 97 | log('1.4 handle: ' + item.name); 98 | if(item.name==='Mike') callback('myerr'); 99 | else callback(null, item.name+'!!!'); 100 | }, item.delay); 101 | }, function(err, results) { 102 | log('1.4 err: ', err); 103 | log('1.4 results: ', results); 104 | }); 105 | // 47.616> 1.4 enter: Jack 106 | // 47.821> 1.4 handle: Jack 107 | // 47.821> 1.4 enter: Mike 108 | // 47.931> 1.4 handle: Mike 109 | // 47.931> 1.4 err: myerr 110 | // 47.932> 1.4 results: [ 'Jack!!!', undefined ] 111 | -------------------------------------------------------------------------------- /nextTick.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | /** 4 | * async.nextTick的作用与nodejs的nextTick一样,都是把某个函数调用放在队列的尾部。但在浏览器端,只能使用setTimeout(callback,0),但这个方法有时候会让其它高优先级的任务插到前面去。 5 | * 6 | * 所以提供了这个nextTick,让同样的代码在服务器端和浏览器端表现一致。 7 | */ 8 | // nextTick(callback) 9 | 10 | var calls = []; 11 | 12 | async.nextTick(function() { 13 | calls.push('two'); 14 | }); 15 | 16 | calls.push('one'); 17 | 18 | async.nextTick(function() { 19 | console.log(calls); // -> [ 'one', 'two' ] 20 | }); -------------------------------------------------------------------------------- /parallel.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | /** 4 | * 并行执行多个函数,每个函数都是立即执行,不需要等待其它函数先执行。传给最终callback的数组中的数据按照tasks中声明的顺序,而不是执行完成的顺序。 5 | * 6 | * 如果某个函数出错,则立刻将err和已经执行完的函数的结果值传给parallel最终的callback。其它未执行完的函数的值不会传到最终数据,但要占个位置。 7 | * 8 | * 同时支持json形式的tasks,其最终callback的结果也为json形式。 9 | */ 10 | // parallel(tasks, [callback]) 11 | 12 | var t = require('./t'); 13 | var log = t.log; 14 | 15 | /** 16 | * 并行执行多个函数,每个函数的值将按函数声明的先后顺序汇成一个数组,传给最终callback。 17 | */ 18 | // 1.1 19 | async.parallel([ 20 | function(cb) { t.fire('a400', cb, 400) }, 21 | function(cb) { t.fire('a200', cb, 200) }, 22 | function(cb) { t.fire('a300', cb, 300) } 23 | ], function (err, results) { 24 | log('1.1 err: ', err); // -> undefined 25 | log('1.1 results: ', results); // ->[ 'a400', 'a200', 'a300' ] 26 | }); 27 | 28 | /** 29 | * 如果中途有个函数出错,则将该err和已经完成的函数值汇成一个数组,传给最终的callback。还没有执行完的函数的值将被忽略,但要在最终数组中占个位置 30 | */ 31 | // 1.2 32 | async.parallel([ 33 | function(cb) { log('1.2.1: ', 'start'); t.fire('a400', cb, 400) }, // 该函数的值不会传给最终callback,但要占个位置 34 | function(cb) { log('1.2.2: ', 'start'); t.err('e200', cb, 200) }, 35 | function(cb) { log('1.2.3: ', 'start'); t.fire('a100', cb, 100) } 36 | ], function(err, results) { 37 | log('1.2 err: ', err); // -> e200 38 | log('1.2 results: ', results); // -> [ , undefined, 'a100' ] 39 | }); 40 | 41 | /** 42 | * 以json形式传入tasks,最终results也为json 43 | */ 44 | // 1.3 45 | async.parallel({ 46 | a: function(cb) { t.fire('a400', cb, 400) }, 47 | b: function(cb) { t.fire('c300', cb, 300) } 48 | }, function(err, results) { 49 | log('1.3 err: ', err); // -> undefined 50 | log('1.3 results: ', results); // -> { b: 'c300', a: 'a400' } 51 | }); 52 | 53 | /** 54 | * 如果中途出错,会将err与已经完成的函数值(汇成一个json)传给最终callback。未执行完成的函数值被忽略,不会出现在最终json中。 55 | */ 56 | // 1.4 57 | async.parallel({ 58 | a: function(cb) { t.fire('a400', cb, 400) }, // 该函数的值不会传给最终的callback 59 | b: function(cb) { t.err('e300', cb, 300) }, 60 | c: function(cb) { t.fire('c200', cb, 200) } 61 | }, function(err, results) { 62 | log('1.4 err: ', err); // -> e300 63 | log('1.4 results: ', results); // -> { c: 'c200', b: undefined } 64 | }); 65 | -------------------------------------------------------------------------------- /queue.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | var t = require('./t'); 3 | var log = t.log; 4 | 5 | /** 6 | * queue相当于一个加强版的parallel,主要是限制了worker数量,不再一次性全部执行。当worker数量不够用时,新加入的任务将会排队等候,直到有新的worker可用。 7 | * 8 | * 该函数有多个点可供回调,如worker用完时、无等候任务时、全部执行完时等。 9 | */ 10 | // queue(worker, concurrency) 11 | 12 | // 定义一个queue,设worker数量为2 13 | var q = async.queue(function(task, callback) { 14 | log('worker is processing task: ', task.name); 15 | task.run(callback); 16 | }, 2); 17 | 18 | // 如果某次push操作后,任务数将达到或超过worker数量时,将调用该函数 19 | q.saturated = function() { 20 | log('all workers to be used'); 21 | } 22 | 23 | // 当最后一个任务交给worker时,将调用该函数 24 | q.empty = function() { 25 | log('no more tasks wating'); 26 | } 27 | 28 | // 当所有任务都执行完以后,将调用该函数 29 | q.drain = function() { 30 | console.log('all tasks have been processed'); 31 | } 32 | 33 | // 放入单个任务 34 | q.push({name:'t1', run: function(cb){ 35 | log('t1 is running, waiting tasks: ', q.length()); 36 | t.fire('t1', cb, 400); // 400ms后执行 37 | }}, function(err) { 38 | log('t1 executed'); 39 | }); 40 | 41 | log('pushed t1, waiting tasks: ', q.length()); 42 | 43 | q.push({name:'t2',run: function(cb){ 44 | log('t2 is running, waiting tasks: ', q.length()); 45 | t.fire('t2', cb, 200); // 200ms后执行 46 | }}, function(err) { 47 | log('t2 executed'); 48 | }); 49 | 50 | log('pushed t2, waiting tasks: ', q.length()); 51 | 52 | // 放入多个任务 53 | q.push([{name:'t3', run: function(cb){ 54 | log('t3 is running, waiting tasks: ', q.length()); 55 | t.fire('t3', cb, 300); // 300ms后执行 56 | }},{name:'t4', run: function(cb){ 57 | log('t4 is running, waiting tasks: ', q.length()); 58 | t.fire('t4', cb, 500); // 500ms后执行 59 | }}], function(err) { 60 | log('t3/4 executed'); 61 | }); 62 | 63 | log('pushed t3/t4, waiting tasks: ', q.length()); 64 | 65 | -------------------------------------------------------------------------------- /reduce.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freewind/async_demo/b084ebd25d7836a36ef2f3f34e54b3c0634bf689/reduce.js -------------------------------------------------------------------------------- /series.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | /** 4 | * series(tasks, [callback]) 5 | * 6 | * 依次执行一个函数数组中的每个函数,每一个函数执行完成之后才能执行下一个函数。 7 | * 8 | * 如果任何一个函数向它的回调函数中传了一个error,则后面的函数都不会被执行,并且将会立刻会将该error以及已经执行了的函数的结果,传给series中最后那个callback。 9 | * 10 | * 当所有的函数执行完后(没有出错),则会把每个函数传给其回调函数的结果合并为一个数组,传给series最后的那个callback。 11 | * 12 | * 还可以json的形式来提供tasks。每一个属性都会被当作函数来执行,并且结果也会以json形式传给series最后的那个callback。这种方式可读性更高一些。 13 | */ 14 | 15 | var t = require('./t'); 16 | var log = t.log; 17 | 18 | /** 19 | * 全部函数都正常执行。每个函数产生的值将按顺序合并为一个数组,传给最终的callback。 20 | */ 21 | // 1.1 22 | async.series([ 23 | function(cb) { t.inc(3, cb); }, 24 | function(cb) { t.inc(8, cb); }, 25 | function(cb) { t.inc(2, cb); } 26 | ], function(err, results) { 27 | log('1.1 err: ', err); // -> undefined 28 | log('1.1 results: ', results); // -> [ 4, 9, 3 ] 29 | }); 30 | 31 | /** 32 | * 中间有函数出错。出错之后的函数不会执行,错误及之前正常执行的函数结果将传给最终的callback。 33 | */ 34 | // 1.2 35 | async.series([ 36 | function(cb) { t.inc(3, cb); }, 37 | function(cb) { t.err('test_err', cb); }, 38 | function(cb) { t.inc(8, cb); } 39 | ], function (err, results) { 40 | log('1.2 err: ', err); // -> test_err 41 | log('1.2 results: ', results); // -> [ 4, undefined ] 42 | }); 43 | 44 | /** 45 | * 如果某个函数传的数据是undefined, null, {}, []等,它们会原样传给最终callback。 46 | */ 47 | // 1.3 48 | async.series([ 49 | function(cb) { t.fire(3, cb);}, 50 | function(cb) { t.fire(undefined, cb); }, 51 | function(cb) { t.fire(null, cb); }, 52 | function(cb) { t.fire({}, cb); }, 53 | function(cb) { t.fire([], cb); }, 54 | function(cb) { t.fire('abc', cb) } 55 | ], function(err, results) { 56 | log('1.3 err: ', err); // -> undefined 57 | log('1.3 results: ', results); // -> [ 3, undefined, null, {}, [], 'abc' ] 58 | }); 59 | 60 | /** 61 | * 以json形式传入tasks。其结果也将以json形式传给最终callback。 62 | */ 63 | async.series({ 64 | a: function(cb) { t.inc(3, cb); }, 65 | b: function(cb) { t.fire(undefined, cb); }, 66 | c: function (cb) { t.err('myerr', cb); }, 67 | d: function (cb) { t.inc(8, cb); } 68 | }, function (err, results) { 69 | log('1.4 err: ', err); // -> myerr 70 | log('1.4 results: ', results); // -> { a: 4, b: undefined, c: undefined } 71 | }); -------------------------------------------------------------------------------- /some.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freewind/async_demo/b084ebd25d7836a36ef2f3f34e54b3c0634bf689/some.js -------------------------------------------------------------------------------- /sortBy.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freewind/async_demo/b084ebd25d7836a36ef2f3f34e54b3c0634bf689/sortBy.js -------------------------------------------------------------------------------- /t.js: -------------------------------------------------------------------------------- 1 | var moment = require('moment'); 2 | 3 | exports.inc = function(n, callback, timeout) { 4 | timeout = timeout || 200; 5 | setTimeout(function() { 6 | callback(null, n+1); 7 | }, timeout); 8 | }; 9 | 10 | exports.fire = function(obj, callback, timeout) { 11 | timeout = timeout || 200; 12 | setTimeout(function() { 13 | callback(null, obj); 14 | }, timeout); 15 | }; 16 | 17 | exports.err = function(errMsg, callback, timeout) { 18 | timeout = timeout || 200; 19 | setTimeout(function() { 20 | callback(errMsg); 21 | }, timeout); 22 | }; 23 | 24 | // utils 25 | exports.log = function(msg, obj) { 26 | process.stdout.write(moment().format('ss.SSS')+'> '); 27 | if(obj!==undefined) { 28 | process.stdout.write(msg); 29 | console.log(obj); 30 | } else { 31 | console.log(msg); 32 | } 33 | }; 34 | 35 | exports.wait = function(mils) { 36 | var now = new Date; 37 | while(new Date - now <= mils); 38 | } -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | var t = require('./t'); 3 | 4 | /** 5 | * 让某一个函数在内存中缓存它的计算结果。对于相同的参数,只计算一次,下次就直接拿到之前算好的结果。 6 | */ 7 | // memoize(fn, [hasher]) 8 | 9 | var slow_fn = function(x, y, callback) { 10 | console.log('start working for: ' + x+','+y); 11 | t.wait(100); 12 | console.log('finished: ' + x+','+y); 13 | callback(null, 'im slow for: '+x+','+y); 14 | }; 15 | 16 | var fn = async.memoize(slow_fn); 17 | 18 | fn('a','b', function(err, result) { 19 | console.log(result); 20 | }); 21 | 22 | // 直接得到之前计算好的值 23 | fn('a','b', function(err, result) { 24 | console.log(result); 25 | }); 26 | 27 | /** 28 | * hasher可以让我们自定义如何根据参数来判断它是否已经在缓存中了。 29 | */ 30 | var fn_hasher = async.memoize(slow_fn, function(x,y) { 31 | return x+y; 32 | }); 33 | 34 | fn_hasher('cd','e', function(err, result) { 35 | console.log(result); 36 | }); 37 | 38 | fn_hasher('c','de', function(err, result) { 39 | console.log(result); // 可以取得前面('cd','e')的计算结果 40 | // im show for: cd,e 41 | }); 42 | 43 | 44 | /** 45 | * 让一个已经被memoize的函数不再缓存结果。 46 | */ 47 | // unmemoize(fn) 48 | 49 | var fn2 = async.unmemoize(fn); 50 | console.log('unmemoized'); 51 | 52 | fn2('a','b', function(err,result) { 53 | console.log(result); 54 | }); 55 | 56 | /** 57 | * 执行某异步函数,并记录它的返回值。试验函数时很方便,不用写那些固定模式的代码。 58 | * 59 | * 如果该函数向回调中传入了多个参数,则每行记录一个。 60 | */ 61 | // log(function, arguments) 62 | 63 | var x = function() { 64 | this.name = 'Freewind'; 65 | } 66 | var hello = function(name, callback) { 67 | setTimeout(function() { 68 | callback(null, 'hello ' + name, 'nice to see you ' + name, x, {a:'123'}); 69 | }, 200); 70 | }; 71 | 72 | async.log(hello, 'world'); 73 | // it prints: 74 | // hello world 75 | // nice to see you world 76 | // [Function] 77 | // { a: '123' } 78 | 79 | // 我不太明白dir与log之间,到底有什么大的差别。 80 | // http://stackoverflow.com/questions/10636866/whats-the-difference-between-async-log-and-async-dir 81 | async.dir(hello, 'world'); 82 | // it prints: 83 | // 'hello world' 84 | // 'nice to see you world' 85 | // [Function] 86 | // { a: '123' } 87 | 88 | /** 89 | * noConflict()仅仅用于浏览器端,在nodejs中没用,这里无法演示。 90 | * 91 | * 它的作用是:如果之前已经在全局域中定义了async变量,当导入本async.js时,会先把之前的async变量保存起来,然后覆盖它。用完之后,调用noConflict()方法,就会归还该值。同时返回async本身供换名使用。 92 | */ 93 | // noConflict() 94 | 95 | /* 96 | // global on the server, window in the browser 97 | var root = this, 98 | previous_async = root.async; 99 | 100 | if (typeof module !== 'undefined' && module.exports) { 101 | module.exports = async; 102 | } 103 | else { 104 | root.async = async; 105 | } 106 | 107 | async.noConflict = function () { 108 | root.async = previous_async; 109 | return async; 110 | }; 111 | */ -------------------------------------------------------------------------------- /waterfall.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | /** 4 | * 按顺序依次执行一组函数。每个函数产生的值,都将传给下一个。如果中途出错,后面的函数将不会被执行。错误信息以及之前产生的结果,将传给waterfall最终的callback。 5 | * 6 | * 这个函数名为waterfall(瀑布),可以想像瀑布从上到下,中途冲过一层层突起的石头。 7 | * 8 | * 注意,该函数不支持json格式的tasks 9 | */ 10 | // async.waterfall(tasks, [callback]); 11 | 12 | var t = require('./t'); 13 | var log = t.log; 14 | 15 | /** 16 | * 所有函数正常执行,每个函数的结果都将变为下一个函数的参数。 17 | * 18 | * 注意,所有的callback都必须形如callback(err, result),但err参数在前面各函数中无需声明,它被自动处理。 19 | */ 20 | // 1.1 21 | async.waterfall([ 22 | function(cb) { log('1.1.1: ', 'start'); cb(null, 3); }, 23 | function(n, cb) { log('1.1.2: ',n); t.inc(n, cb); }, 24 | function(n, cb) { log('1.1.3: ',n); t.fire(n*n, cb); } 25 | ], function (err, result) { 26 | log('1.1 err: ', err); // -> null 27 | log('1.1 result: ', result); // -> 16 28 | }); 29 | 30 | /** 31 | * 中途有函数出错,其err和产生的值将直接传给最终callback,后面的函数不再执行。 32 | */ 33 | // 1.2 34 | async.waterfall([ 35 | function(cb) { log('1.2.1: ', 'start'); cb(null, 3); }, 36 | function(n, cb) { log('1.2.2: ', n); t.inc(n, cb); }, 37 | function(n, cb) { log('1.2.3: ', n); t.err('myerr', cb); }, 38 | function(n, cb) { log('1.2.4: ', n); t.fire(n, cb); } 39 | ], function (err, result) { 40 | log('1.2 err: ', err); // -> myerr 41 | log('1.2 result: ', result); // -> undefined 42 | }); 43 | 44 | /** 45 | * 注意: 以json形式传入tasks,将不会被执行!! 46 | */ 47 | async.waterfall({ 48 | // these tasks won't be invoked 49 | a: function(cb) { log('1.3.1: ', 'start'); cb(null, 3); }, 50 | b: function(n, cb) { log('1.3.2: ', n); t.inc(n, cb); }, 51 | c: function(n, cb) { log('1.3.3: ', n); t.fire(n*n, cb); } 52 | }, function (err, result) { 53 | log('1.3 err: ', err); // -> undefined 54 | log('1.3 result: ', result); // -> undefined 55 | }); -------------------------------------------------------------------------------- /whilst_until.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | var t = require('./t'); 3 | var log = t.log; 4 | 5 | /** 6 | * 相当于while,但其中的异步调用将在完成后才会进行下一次循环。 7 | * 8 | * 它相当于: 9 | * try { 10 | * whilst(test) { 11 | * fn(); 12 | * } 13 | * callback(); 14 | * } catch (err) { 15 | * callback(err); 16 | * } 17 | * 18 | * 该函数的功能比较简单,条件变量通常定义在外面,可供每个函数访问。在循环中,异步调用时产生的值实际上被丢弃了,因为最后那个callback只能传入错误信息。 19 | * 20 | * 另外,第二个函数fn需要能接受一个函数cb,这个cb最终必须被执行,用于表示出错或正常结束。 21 | * 22 | */ 23 | // whilst(test, fn, callback) 24 | 25 | 26 | /** 27 | * 正常情况,没有出错。第二个函数虽然是异步调用,但被同步执行。所以第三个函数被调用时,已经过了3秒。 28 | */ 29 | // 1.1 30 | var count1 = 0; 31 | async.whilst( 32 | function() { return count1 < 3 }, 33 | function(cb) { 34 | log('1.1 count: ', count1); 35 | count1++; 36 | setTimeout(cb, 1000); 37 | }, 38 | function(err) { 39 | // 3s have passed 40 | log('1.1 err: ', err); // -> undefined 41 | } 42 | ); 43 | 44 | /** 45 | * 中途出错。出错后立刻调用第三个函数。 46 | */ 47 | // 1.2 48 | var count2 = 0; 49 | async.whilst( 50 | function() { return count2 < 3 }, 51 | function(cb) { 52 | log('1.2 count: ', count2); 53 | if(count2===1) { 54 | t.err('myerr', cb, 200); 55 | } else { 56 | count2++; 57 | setTimeout(cb, 1000); 58 | } 59 | }, 60 | function(err) { 61 | // 2s have passed 62 | log('1.2 err: ', err); // -> myerr 63 | } 64 | ); 65 | 66 | /** 67 | * 第二个函数即使产生值,也会被忽略。第三个函数只能得到err。 68 | */ 69 | // 1.3 70 | var count3 = 0; 71 | async.whilst( 72 | function() { return count3 < 3 }, 73 | function(cb) { 74 | log('1.3 count: ', count3); 75 | t.inc(count3++, cb); 76 | }, 77 | function(err,result){ // result没有用 78 | log('1.3 err: ', err); // -> undefined 79 | log('1.3 result: ', result); // -> undefined 80 | } 81 | ); 82 | 83 | /** 84 | * until与whilst正好相反,当test为false时循环,与true时跳出。其它特性一致。 85 | */ 86 | // 1.4 87 | var count4 = 0; 88 | async.until( 89 | function() { return count4>3 }, 90 | function(cb) { 91 | log('1.4 count: ', count4); 92 | count4++; 93 | setTimeout(cb, 200); 94 | }, 95 | function(err) { 96 | // 4s have passed 97 | log('1.4 err: ',err); // -> undefined 98 | } 99 | ); --------------------------------------------------------------------------------