概要
Async.js 是一个工具模块,提供了直接而强大的 JavaScript 异步功能。虽然是为 Node.js 设计的,但是它也可以直接在浏览器中使用。
Async 提供了大约20个函数,包括一些常用的集合处理函数(map, reduce, filter, forEach 等等),也有常用的异步流程控制函数(parallel , series, waterfall 等等)。最后需要注意的是:Async工具集里面所有函数的最后一个参数都被设计成回调函数。
Async工具集包含三大类工具:Collections(集合处理工具),Control Flow(流程控制工具),Utils(常用工具)。下面就开始API和Demo的内容了。
Collections
each
each(arr, iterator, callback)
该函数用于并行迭代数组的每个元素,并行迭代是针对迭代器内部有异步操作而言的(比如setTimeout或者ajax),而对数组每个元素的迭代调用是顺序的。因为是并行迭代,迭代器内部在存在异步操作的情况下,无法保证对各个元素的迭代完成时间是顺序的。
Arguments
- arr - 需要迭代的数组。
- iterator(item, callback) - 迭代器,用于处理每个元素。迭代器最后必须显式调用callback(err)。如果想立刻触发主回调函数的执行(通常是发生了错误后这样处理),传入非空实参即可;否则只要写上 callback() 或者 callback(null) 即可。
- callback - 主回调函数,在所有元素的迭代完成后自动调用;或者在某个元素进行迭代时,向迭代器传递非空实参时,会被立刻调用。
Examples
- var items = ["1","2","3"];
- // 1.我们用setTimeout来模拟异步
- // 2.同时为了说明“并行迭代存在异步的情况下,每个元素的迭代完成的时间将无法保证”
- // 我们让第一个元素延时300ms执行,第二个延时200ms,第三个延时100ms
- async.each(items, function( item, callback) {
- console.log('Processing item : ' + item);
- if(item==="2" ) {
- // 假设在迭代第二个元素我们需要抛出一个错误
- setTimeout(function(){
- console.log('the second item is the bad one!');
- // 向callback传递一个非空实参,可以立刻触发总回调函数的执行
- callback('the second item is the bad one!');
- },100*(4-item));
- } else {
- // 假设其它元素都是正常的
- setTimeout(function(){
- console.log(item + ' is processed!');
- callback();
- },100*(4-item));
- }
- }, function(err){
- // err是由迭代器的回调函数传入
- if(err) {
- // 在迭代某个元素是传入了err.
- // 官网的注释说是传入错误的话,后面的迭代将会终止,而事实并未终止,"1 is processed!"依旧被输出了.
- console.log('Failed to process : ' + err);
- }else{
- console.log('All items have been processed successfully');
- }
- });
- /**
- * 输出结果:
- * Processing item : 1
- * Processing item : 2
- * Processing item : 3
- * 3 is processed!
- * the second item is the bad one!
- * Failed to process : the second item is the bad one!
- * 1 is processed!
- *
- * 结论:
- * 1.每个元素的迭代开始时间是跟数组元素顺序一致的,即前面的元素一定先开始迭代。
- * 2.每个元素的迭代完成时间跟数组元素的顺序不一定一致,本例中,先输出的是“3 is processed!”
- * 3.主回调只会被执行一次,其执行时间点受到迭代器回调传入的实参影响
- * 4.当前元素的迭代过程中向迭代器回调抛出错误并不会终止后面元素的迭代
- */
eachSeries
eachSeries(arr, iterator, callback)
实质就是串行的each函数,有两个不同点:
- eachSeries 函数是串行执行的,前面元素的迭代完成之前不会开始后面元素的迭代。这种情况不太适合大批量无关联的网络请求,IO阻塞耗时。
- 某个元素迭代时抛出了错误,主回调被触发,后面的元素不会再迭代。
Examples
- var items = ["1","2","3"];
- async.eachSeries(items, function( item, callback) {
- console.log('Processing item : ' + item);
- setTimeout(function(){
- if(item==="2" ) {
- // 假设在迭代第二个元素我们需要抛出一个错误
- console.log('the second item is the bad one!');
- callback('the second item is the bad one!');
- }else{
- // 假设其它元素都是正常的
- console.log(item + ' is processed!');
- callback();
- }
- },100*(4-item));
- }, function(err){
- if(err) {
- // 第二个元素抛出了err.整个迭代也被终止了
- console.log('Failed to process : ' + err);
- }else{
- console.log('All items have been processed successfully');
- }
- });
- /**
- * 输出结果:
- * Processing item : 1
- * 1 is processed!
- * Processing item : 2
- * the second item is the bad one!
- * Failed to process : the second item is the bad one!
- *
- * 结论:
- * 1.每个元素的迭代是顺序进行的,当前元素不完成不会开始下一个元素的迭代(完成时间跟数组元素的顺序一致)
- * 2.当前元素迭代过程中抛出错误,后面的元素的迭代不会再进行
- */
eachLimit
eachLimit(arr, limit, iterator, callback)
each函数的增强版,增加了一个limit参数,用于限制并行执行的最大数量。
举个简单的例子:
一个卫生间只有4个坑,一下子却来了6个人,所以先来的4个人先去把坑占了(可以理解为数组中前4个元素首先按照顺序开始迭代)。
因为每个人拉屎的速度是不一样的,所以第一个进坑的人不一定第一个出来(理解为先开始迭代的元素不一定先结束迭代,也就是元素迭代的完成时间是无序的)。
某个时刻前4个蹲坑的人,有一个拉完了,第5个人,立刻补上,占了那个已经空缺的坑。就这样空闲一个坑,便填进去一个人,直到6个人全部拉完屎。
不过事情并不是总会那么完美,如果有个人在拉屎过程中出了问题,比如说他忘记带卫生纸了,这时候他得求援了,所以他把这个没有纸擦屁股的问题抛给了卫生间收费的老大爷(这个老大爷,我们可以理解为主回调函数),老大爷卖了包卫生纸给他。
最后需要说明的是,不是有个人没带卫生纸,排队的等候的人就不拉屎了,所有人最终都是要占坑拉屎的。
Arguments
- arr - 需要迭代的数组。
- limit - 可以同时并行迭代的最大数量。
- iterator(item, callback) - 迭代器,用于处理每个元素。迭代器最后必须显式调用callback(err)。如果想立刻触发主回调函数的执行(通常是发生了错误后这样处理),传入非空实参即可;否则只要写上 callback() 或者 callback(null) 即可。
- callback - 总回调函数,在所有元素的迭代完成后自动调用;或者在某个元素进行迭代时,向迭代器传递非空实参时,会被立刻调用。
Examples
- // 6个拉屎的人
- var items = ["1.李世民","2.长孙无忌","3.房玄龄","4.李靖","5.岑文本","6.侯君集"];
- // 只有4个坑
- async.eachLimit(items, 4,function( item, callback) {
- console.log(item.substring(2) + " 开始占坑……");
- $.post("../json/" + item.substring(0,1) + ".json", {}, function(resp){
- var action = resp.action;
- var name = resp.name;
- // 房玄龄没带纸
- if(action=="没带纸"){
- console.log(name + ' 没带纸!!!!!!!!!!!!!');
- // 呼叫厕所收费的老头
- callback(name + ' !!!!!!!!!!!!!');
- }else{
- console.log(name + ' 拉完了!');
- callback();
- }
- });
- }, function(err){
- if(err) {
- console.log('厕所里面发生大事了 : ' + err);
- }else{
- console.log('每个人都顺利滴拉完了屎……');
- }
- });
- /**
- * 输出结果:
- * 李世民 开始占坑……
- * 长孙无忌 开始占坑……
- * 房玄龄 开始占坑……
- * 李靖 开始占坑……
- * 李世民 拉完了!
- * 岑文本 开始占坑……
- * 长孙无忌 拉完了!
- * 侯君集 开始占坑……
- * 房玄龄 没带纸!!!!!!!!!!!!!
- * 厕所里面发生大事了 : 房玄龄 没带纸!!!!!!!!!!!!!
- * 李靖 拉完了!
- * 岑文本 拉完了!
- * 侯君集 拉完了!
- *
- * 结论:
- * 1.数组的前limit个元素先开始迭代。
- * 2.每个元素的迭代完成时间是无序的
- * 3.如果有迭代完成的,则在排队中的元素自动开始下一个的迭代
- */
管理员 世纪之光 : 2015年06月30日23:11:32 地下1层