node.js async

async 是基于node.js 一个流程控制包,我们常用的主要是为了适应异步编程,减少回调的嵌套,控制函数同步或者控制函数异步后执行后续操作的功能。

async 库:https://github.com/caolan/async

安装:npm install async

使用:var async = require('async)

.....

1、Collections 集合

1.1 each

1.1.1each(arr, iterator, callback)

遍历集合元素并做相同的异步的操作。遍历过程有异常通过cb返回,进程不会中断,直到全部遍历完成后err在最后的回调函数返回

async.each([1, 2, 3, 4, 5, 6, 7, 8], function(item, cb) {
	if (item == 4) {
		cb('is ' + item);
	} else {
		cb();
	}
}, function(err) {
	console.info('error==>' + err);
});

1.1.2 eachSeries(arr, iterator, callback)

顺序遍历集合。一个元素遍历完成及后续的操作完成后,才会进入下一个元素遍历。遍历过程出现err中断循环并在最后的回调函数返回err

async.eachSeries([1, 2, 3, 4, 5, 6, 7, 8], function(item, cb) {
	if (item == 4) {
		cb('throw err: ' + item);
	} else {
		cb();
	}
}, function(err) {
	console.info('catch err: ' + err);
});

1.1.3 eachLimit(arr, limit, iterator, callback)

批量遍历集合。同一时候只会执行limit个任务。遍历过程有异常通过cb返回,进程不会中断,直到全部遍历完成后err在最后的回调函数返回。

async.eachLimit([1, 2, 3, 4, 5, 6, 7, 8], 2, function(item, cb) {
	console.info(item);
	if (item == 4) {
		cb('throw err: ' + item);
	} else {
		cb();
	}
}, function(err) {
	console.info('catch err: ' + err);
});

1.2 map

1.2.1 map(arr, iterator, callback)

对集合中的每一个元素,执行某个异步操作,得到结果。所有的结果以数组的方式汇总到最终的callback里。与each的区别是,each只关心操作不管最后的值,而map关心的最后产生的值。执行任务的时候,如果存在err,循环不会中断,会通过cb返回err,但最终的callback只能返回err前的数据。

async.map([1, 2, 3, 4, 5, 6, 7, 8], function(item, cb) {
	console.info(item);
	if (item == 4) {
		cb('4 is bad');
	} else {
		cb(null, 'transformed ' + item);
	}
}, function(err, result) {
	console.info('error:' + err);
	console.info('result:' + result);
});

1.2.2 mapSeries(arr, iterator, callback)

同map。区别是mapSeries是顺序执行的,执行任务的时候,如果存在err,通过cb返回并且中断循环,最终的callback只能返回err前的数据。

async.mapSeries([1, 2, 3, 4, 5, 6, 7, 8], function(item, cb) {
	console.info(item);
	if (item == 4) {
		cb('4 is bad');
	} else {
		cb(null, 'transformed ' + item);
	}
}, function(err, result) {
	console.info('error:' + err);
	console.info('result:' + result);
})

1.2.3 mapLimit(arr, limit, iterator, callback)

同map。区别在多了一个参数limit同一时刻允许的并发数

async.mapLimit([1, 2, 3, 4, 5, 6, 7, 8], 2,function(item, cb) {
	console.info(item);
	if (item == 4) {
		cb('4 is bad');
	} else {
		cb(null, 'transformed ' + item);
	}
}, function(err, result) {
	console.info('error:' + err);
	console.info('result:' + result);
});

1.3 filter & reject

1.3.1 filter(arr, iterator, callback)

过滤array中的元素,iterator中的cb是一个表达式,只有两个结果false和true,结果为true汇总在最后的callback

async.filter([1, 5, 3, 7, 2], function(item, cb){  
    cb(item > 3);  
}, function(results){  
    console.log(results);// [5, 7]  
}); 

1.3.2 filterSeries(arr, iterator, callback)

类似filter,这里是按array元素的先后顺序过滤。

1.3.3 reject(arr, iterator, callback)

和filter相反,filter是保留t的表达式为true的item,而reject是表达式值为false的item

async.reject([1, 5, 3, 7, 2], function(item, cb) {
	cb(item > 3);
}, function(results) {
	console.log(results); // [1,3,2]  
});

1.3.4 rejectSeries(arr, iterator, callback)

类似reject

1.4 reduce & reduceRight

1.4.1 reduce(arr, memo, iterator, callback)

reduce: 可以让我们给定一个初始值,用它与集合中的每一个元素做运算,最后得到一个值。reduce从左向右来遍历元素,如果想从右向左,可使用reduceRight。

async.reduce([3, 2, 1], 1, function(memo, item, callback) {
	console.info(memo + '~~~' + item);
	callback(null, memo + item)
}, function(err, result) {
	if (err) {
		console.error("error: " + err);
		return;
	}
	console.log(result); // 7  
});

1~~~3
4~~~2
6~~~1
7

1.5 detect和detectSeries

用于取得集合中满足条件的第一个元素。这个API很像filter,参数也都一样,但是只会第一个结果

async.detect([13, 44, 23], function(item, callback) {
	callback(item > 37);
}, function(result) {
	console.log(result); // 44  
});

1.6 sortBy

sortBy(arr, iterator, callback)

数组排序,数组可以是数字,字符串,或者对象

//升序
async.sortBy([1, 9, 3, 5], function(x, callback) {
    callback(null, x);
}, function(err, result) {
    console.info(result); //[ 1, 3, 5, 9 ]
});

//降序
async.sortBy([1, 9, 3, 5], function(x, callback) {
    callback(null, x * -1); //<- x*-1 instead of x, turns the order around
}, function(err, result) {
    console.info(result);//[ 9, 5, 3, 1 ]
});

//数组元素可以是对象,按照age或者name属性排序
var person1 = {"name": "aaa", age:79};  
var person2 = {"name": "bbb", age:23};  
var person3 = {"name": "ccc", age:54};  
  
async.sortBy([person1, person2, person3], function(person, callback){  
    callback(null, person.age);  
}, function(err, sorted){  
    console.log(sorted);  
}); 

============结果==============
[ { name: 'bbb', age: 23 },
  { name: 'ccc', age: 54 },
  { name: 'aaa', age: 79 } ]

1.7 some

some(arr, iterator, callback)

当集合中是否有至少一个元素满足条件时,最终callback得到的值为true,否则为false.类似于filter和detect。区别在于,filter和detect是返回找到的元素,而some是返回bool

async.some([1, 5, 9], function(item, callback) {
	callback(item > 10);
}, function(result) {
	console.log(result); // false  
});

1.8 every

every(arr, iterator, callback)

如果集合里每一个元素都满足条件,则传给最终回调的result为true,否则为false

async.every([1, 21, 23], function(item, callback){  
    callback(item > 10);  
}, function(result){  
    console.log(result);// false  
}); 

1.9 concat & concatSeries

concat(arr, iterator, callback) 异步

concatSeries(arr, iterator, callback) 同步顺序执行

将多个异步操作的结果合并为一个数组。

async.concat([1, 2, 3], function(item, callback) {
	callback(null, [item + 1, item + 2]);
}, function(err, results) {
	console.log(results); // [2, 3, 3, 4, 4, 5];  
});

2、流程控制

2.1 series

一组函数顺序执行,这个API很常用。当一个步骤完成时,调用callback(),不传递参数;如果其中一个步骤出错,则调用callback(err),后续的步骤就不会继续执行。每个步骤执行的结果,会汇总到最终callback的results参数中

var async = require('async');
async.series([
    function(callback) {
        console.log("first task");
        callback(null, 1);
    },
    function(callback) {
        console.log("second task");
        callback({
            message: "some error"
        }, 2); // callback with a error parameter  
    },
    function(callback) {
        console.log("third task"); // not called  
        callback(null, 3);
    }
], function(error, results) {
    if (error) {
        console.error("error happend: ",error);
    }
    console.log(results); // [1, 2]  
});
结果:

first task
second task
error happend:  { message: 'some error' }
[ 1, 2 ]

2.2 parallel

并行执行多个函数,每个函数都是立即执行,不需要等待其它函数先执行。传给最终callback的数组中的数据按照tasks中声明的顺序,而不是执行完成的顺序。

var async = require('async');
async.parallel([
    function(callback) {
        console.log("first task");
        callback(null, 1);
    },
    function(callback) {
        console.log("second task");
        callback({
            message: "some error"
        }, 2); // callback with a error parameter  
    },
    function(callback) {
        console.log("third task"); // not called  
        callback(null, 3);
    }
], function(error, results) {
    if (error) {
        console.error("error happend: ", error);
    }
    console.log(results); // [1, 2]  
});
结果:
first task
second task
error happend:  { message: 'some error' }
[ 1, 2 ]
third task

2.3 whilst

whilst(test, fn, callback)

相当于while,重复执行一个函数。当test函数为true时,重复调用fn。直到test函数为false或者fn函数有错误产生时候,调用最后的callback

var async = require('async');
var count = 0;

async.whilst(
    function() {
        console.info('111==>' + count);
        return count < 5;
    },
    function(callback) {
        console.info('222==>' + count);
        count++;
        setTimeout(callback, 1000);
    },
    function(err) {
        // 5 seconds have passed
    }
);
结果:

111==>0
222==>0
111==>1
222==>1
111==>2
222==>2
111==>3
222==>3
111==>4
222==>4
111==>5

2.4 doWhilst

doWhilst: 相当于do…while, doWhilst交换了fn,test的参数位置,先执行一次循环,再做test判断。

var async = require('async');
var count = 0;

async.doWhilst(
    function(callback) {
        console.info('111=>'+count);
        count++;
        setTimeout(callback, 1000);
    },
    function() {
        console.info('222=>'+count);
        return count < 5;
    },
    function(err) {
        // 5 seconds have passed
    }
);

结果:

111=>0
222=>1
111=>1
222=>2
111=>2
222=>3
111=>3
222=>4
111=>4
222=>5

2.5 until

until(test, fn, callback)

Repeatedly call `fn` until `test` returns `true`. Calls `callback` when stopped,
or an error occurs.

until与whilst正好相反,当test为false时循环,与true时跳出。其它特性一致。

2.6 doUntil

doUntil(fn, test, callback)

Like [`doWhilst`](#doWhilst), except the `test` is inverted. Note the argument ordering differs from `until`.

doUntil与doWhilst正好相反,当test为false时循环,与true时跳出。其它特性一致。

2.7 forever

forever(fn, errback)

循环执行一个函数,直到抛出错误

var async = require('async');
var flag = 0;
async.forever(function(callback) {
    setTimeout(function() {
        console.log("forever...");
        console.log(flag);
        flag++;
        if (flag > 5) {
            callback({
                message: "hello"
            });
        } else {
            callback();
        }
    }, 1000);

}, function(err) {
    // once this callback be called, the "forever" stops  
    if (err) {
        console.error("there is an error", err);
    }
});

2.8 waterfall

waterfall(tasks, [callback])

同步执行多个函数,函数间可传值。

可以解决callback嵌套的问题。上一个流程的执行结果,会传给下一个流程的参数。如果其中一个流程出错,则会中止后续流程的执行,直接调用最终的callback。否则最后一个流程的结果,会传递给最终callback

var async = require('async');
async.waterfall([
    function(callback) {
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback) {
        // arg1 now equals 'one' and arg2 now equals 'two'
        callback(null, 'three');
    },
    function(arg1, callback) {
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function(err, result) {
    // result now equals 'done'    
});

2.9 compose

compose(fn1, fn2...)

将几个函数组合成一个函数,例: `f()`, `g()`, and `h()` 有这么几个函数,组合后f(g(h()))。外层的参数来自内层的返回值。

var async = require('async');

function add1(n, callback) {
    setTimeout(function() {
        console.info('add1:' + (n + 1));
        callback(null, n + 1);
    }, 10);
}

function mul3(n, callback) {
    setTimeout(function() {
        console.info('mul3:' + n * 3);
        callback(null, n * 3);
    }, 10);
}

var add1mul3 = async.compose(mul3, add1);
add1mul3(4, function(err, result) {
    console.info('the end:' + result);
});

结果:

add1:5
mul3:15
the end:15

2.10 applyEach

applyEach(fns, args..., callback)

实现给一数组中每个函数传相同参数,通过callback返回。如果只传第一个参数,将返回一个函数对象,我可以传参调用。

applyEachSeries 类似applyEach,fns中的函数同步执行

async.applyEach([
    function(data1) {
        console.info('data1:' + data1)
    },
    function(data2) {
        console.info('data2:' + data2)
    }
], ['hello'], function(err, result) {

});

结果:

data1:hello
data2:hello

2.11 seq

seq(fn1, fn2...)

不明。

2.12 queue

queue(worker, concurrency)

任务队列,异步执行,可以设置统一时刻允许异步执行任务的并发数。每个任务完成后执行一个cb返回给queue。

* `length()` - 等待处理的任务数
* `started` - 任务是否被添加并且被队列处理
* `running()` -返回当前正在处理的项目数。
* `idle()` - 返回队列中是否有等待处理的任务,布尔型,true false
* `concurrency` - 任务并发数参数
* `push(task, [callback])` - 添加一个新的任务入队列,任务完成时候cb。
* `unshift(task, [callback])` - 添加一个新任务放进队列的最前端
* `saturated` - 当队列中的任务数量与并发参数相同时触发
* `empty` - 最后一个任务入队时触发empty.
* `drain` - 队列中全部执行完成后触发
* `paused` - 查看队列是否处在暂停状态,布尔值
* `pause()` - 暂停队列运行
* `resume()` - 恢复队列运行.
* `kill()` - 强制队列中的任务空闲并且等待执行

// create a queue object with concurrency 2

var q = async.queue(function (task, callback) {
    console.log('hello ' + task.name);
    callback();
}, 2);


// assign a callback
q.drain = function() {
    console.log('all items have been processed');
}

// add some items to the queue

q.push({name: 'foo'}, function (err) {
    console.log('finished processing foo');
});
q.push({name: 'bar'}, function (err) {
    console.log('finished processing bar');
});

// add some items to the queue (batch-wise)

q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {
    console.log('finished processing bar');
});

// add some items to the front of the queue

q.unshift({name: 'bar'}, function (err) {
    console.log('finished processing bar');
});

2.13 priorityQueue

类似queue,priority优先权是一个数字,决定队列中任务执行的优先顺序。
如果push的是一个数组,优先权一致。

var async = require('async');

var q = async.priorityQueue(function(task, callback) {
    setTimeout(function() {
        console.log('hello ' + task.name);
        callback()
    }, 1000);
}, 30);

for (var i = 0; i < 10; i++) {
    if (i == 5) {
        q.push({
            name: 'foo' + i
        }, 1, function(err) {});
    } else {
        q.push({
            name: 'foo' + i
        }, 2, function(err) {});
    }
}

结果:

hello foo5
hello foo0
hello foo1
hello foo2
hello foo3
hello foo4
hello foo6
hello foo7
hello foo8
hello foo9

2.14 cargo

cargo(worker, [payload])

一个串行的消息队列,类似于queue,通过限制了worker数量,不再一次性全部执行。不同之处在于,cargo每次会加载满额的任务做为任务单元,只有任务单元中全部执行完成后,才会加载新的任务单元。

var async = require('async');

var cargo = async.cargo(function(tasks, callback) {
    for (var i = 0; i < tasks.length; i++) {
        console.log('hello ' + tasks[i].name);
    }
    callback();
}, 2);

for (var i = 0; i < 8; i++) {
    cargo.push({
        name: 'foo' + i
    }, function(err) {
        console.log('finished processing foo' + i);
    });
}

结果:

hello foo0
hello foo1
finished processing foo8
finished processing foo8
hello foo2
hello foo3
finished processing foo8
finished processing foo8
hello foo4
hello foo5
finished processing foo8
finished processing foo8
hello foo6
hello foo7
finished processing foo8
finished processing foo8

2.15 auto

auto(tasks, [callback]) 多个函数有依赖关系,有的并行执行,有的依次执行

用来处理有依赖关系的多个任务的执行。比如某些任务之间彼此独立,可以并行执行;但某些任务依赖于其它某些任务,只能等那些任务完成后才能执行。

虽然我们可以使用async.parallel和async.series结合起来实现该功能,但如果任务之间关系复杂,则代码会相当复杂,以后如果想添加一个新任务,也会很麻烦。这时使用async.auto,则会事半功倍。

如果有任务中途出错,则会把该错误传给最终callback,所有任务(包括已经执行完的)产生的数据将被忽略。

这里假设我要写一个程序,它要完成以下几件事:

  1. 从某处取得数据
  2. 在硬盘上建立一个新的目录
  3. 将数据写入到目录下某文件
  4. 发送邮件,将文件以附件形式发送给其它人。

分析该任务,可以知道1与2可以并行执行,3需要等1和2完成,4要等3完成。

var async = require('async');

async.auto({
    get_data: function(callback) {
        console.log('in get_data');
        // async code to get some data
        callback(null, 'data', 'converted to array');
    },
    make_folder: function(callback) {
        console.log('in make_folder');
        // async code to create a directory to store a file in
        // this is run at the same time as getting the data
        callback(null, 'folder');
    },
    write_file: ['get_data', 'make_folder',
        function(callback, results) {
            console.log('in write_file', JSON.stringify(results));
            // once there is some data and the directory exists,
            // write the data to a file in the directory
            callback(null, 'filename');
        }
    ],
    email_link: ['write_file',
        function(callback, results) {
            console.log('in email_link', JSON.stringify(results));
            // once the file is written let's email a link to it...
            // results.write_file contains the filename returned by write_file.
            callback(null, {
                'file': results.write_file,
                'email': 'user@example.com'
            });
        }
    ]
}, function(err, results) {
    console.log('err = ', err);
    console.log('results = ', results);
});

结果:

in get_data
in make_folder
in write_file {"get_data":["data","converted to array"],"make_folder":"folder"}
in email_link {"get_data":["data","converted to array"],"make_folder":"folder",
write_file":"filename"}
err =  null
results =  { get_data: [ 'data', 'converted to array' ],
  make_folder: 'folder',
  write_file: 'filename',
  email_link: { file: 'filename', email: 'user@example.com' } }

2.16 retry

retry([times = 5], task, [callback])

重试任务,task存在err则重试,没有err只执行一次,times重试次数,默认5次

task存在error,cb (error):

async.retry(3, function(cb, result) {
    console.info(new Date());
    cb('err');
}, function(err, result) {
    // do something with the result
});

结果:

Sun Feb 22 2015 02:30:12 GMT+0800 (中国标准时间)
Sun Feb 22 2015 02:30:12 GMT+0800 (中国标准时间)
Sun Feb 22 2015 02:30:12 GMT+0800 (中国标准时间)

task 不存在error,cb() :

async.retry(3, function(cb, result) {
    console.info(new Date());
    cb();
}, function(err, result) {
    // do something with the result
});

结果:Sun Feb 22 2015 02:31:26 GMT+0800 (中国标准时间)

2.17 iterator

iterator(tasks)

iterator函数迭代器,task是一个函数数组,通过迭代iterator来执行函数

var iterator = async.iterator([
    function(){ console.info('one'); },
    function(){ console.info('two'); },
    function(){ console.info('three'); }
]);

var iterator2 = iterator(); //one
var iterator3 = iterator2(); //tow
iterator3();//three

2.18 apply

apply(function, arguments..)

给一个函数预绑定多个参数并生成一个可直接调用的新函数,简化代码。

对于函数

function(callback) { t.inc(3, callback); }
可以用apply改写为:

async.apply(t.inc, 3);
还可以给某些函数预设值,得到一个新函数:

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

2.19 nextTick

node里其实已经有nextTick方法,这个方法是为了统一node和浏览器环境的行为,在浏览器环境,实际调用的是setTimeout(func, 0)函数

var call_order = [];
async.nextTick(function(){
    call_order.push('two');
    // call_order now equals ['one','two']
});
call_order.push('one')

2.20 times & timesSeries

times 异步运行,times可以指定调用几次,并把结果合并到数组中返回;

timesSeries 同步运行

    async.times(5, function(n, next){  
        console.log("n: " + n);  
        next(null, n * 2);  
    }, function(err, results){  
        console.log(results);// [0, 2, 4, 6, 8]  
    });  

3、工具类 Utils

memoize: 让某一个函数在内存中缓存它的计算结果。对于相同的参数,只计算一次,下次就直接拿到之前算好的结果。
unmemoize: 让已经被缓存的函数,返回不缓存的函数引用。
dir: 与log类似,不同之处在于,会调用浏览器的console.dir()函数,显示为DOM视图。
noConflict: 如果之前已经在全局域中定义了async变量,当导入本async.js时,会先把之前的async变量保存起来,然后覆盖它。仅仅用于浏览器端,在nodejs中没用,这里无法演示。
log: 执行某异步函数,并记录它的返回值,日志输出。


参考文献:

http://blog.csdn.net/jbboy/article/details/37667809

http://kyfxbl.iteye.com/blog/2018238

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值