从零学习node.js之详解异步控制工具async(八)


Posted in Javascript onFebruary 27, 2017

前言

大家在编写异步程序时,最头痛的就是不知道结果什么时候返回给我们,然后执行后面的操作,很多时候只能把后面的操作放到返回成功的函数里,或者使用计数器等方法。

比较典型的两个就是:后面的操作需要依赖上一个异步操作的结果;多个异步操作并行执行,都执行完成后再执行接下来的操作。

这两个操作中,第一个异步的程序我们可能会写成这样:

db.select(SQL1, function(res1){
 db.delete(SQL2, function(res2){
 db.insert(SQL3, function(res3){
 // ...
 })
 })
});

将后面的操作写到执行成功后的回调函数里。第2个并行的异步操作,可以使用计数器的方法,每个异步调用成功时,计数器加1,当所有的异步都调用成功后,再接着执行:

var count = 0;
var success = function(){
 count++;
 if(count>=3){ 
  console.log('执行完毕...');
 }
}

var select = function(){
 db.select(sql, function(res){
  success();
 })
}
var select2 = function(){
 db.select(sql, function(res){
  success();
 })
}
var select3 = function(){
 db.select(sql, function(res){
  success();
 })
}
select();
select2();
select3();

这些编写方式非常麻烦,而且代码逻辑比较混乱,调试起来也很不方便。那么就要用到异步控制的利器async了。

介绍

async的作用是进行流程的控制,而且提供了非常多的方法可供调用。

这些方法可以分为三大类:

  • 集合类(Collections)
  • 流程控制类(Control Flow)
  • 工具类(Utils)

下面我们从这三个分类里分别挑出几个方法进行讲解。

二、函数介绍

async中提供了非常多的方法可供使用,我们仅仅是讲解其中几个比较有代表性的,其他的可以访问官方文档:http://caolan.github.io/async/docs.html。

2.1 集合类

集合类中的方法主要有some, ‘map', ‘each', ‘every'等,这些方法是对数组或组合进行某个相同的操作后,统一执行回调函数。

我们以map为例,map对集合中的每一个元素,执行某个相同的异步操作,得到结果。所有的结果将汇总到最终的callback里。
使用方法,map接收三个参数,分别是:

参数名称 类型 说明
coll iteratee callback
Array | Iterable | Object function function
需要处理数组,集合或其他可迭代的类型 迭代方法,用来对集合中的每一项进行处理。该方法接收两个参数(item, callback);item为集合中的每一项, callback为回调函数。callback需要带有err(有时可能为null)和处理后的数据,callback(err, data) 最终回调函数,当集合处理完毕后调用此函数,传递两个参数err和result,result为之前处理后的所有的结果的集合

注意:中间处理函数iteratee对coll中的每一项都是并发处理的,因此并不能保证iteratee按照顺序完成。不过,如果coll是个数组,最后的结果集results会按照coll中的顺序排列;如果coll是个集合(Object)类型,results会是数组类型,结果将大致按照coll的键的顺序排列(但是不同在不同的JavaScript引擎中会有可能发生变化)。

我们来举个例子,使用map获取几个文件中的内容:

var files = ['./file/cnode_1.txt', './file/cnode_2.txt', './file/cnode_3.txt'];

// 读取文件内容
// 第1个参数 文件名称列表的数组
// 第2个参数 传入数组中的每一项和回调函数
// 第3个参数 results为所有结果的集合
async.map(files, function(file, cb){
 fs.readFile(file, 'utf-8', function(err, data){
  cb(err, data);
 })
}, function(err, results){
 console.log( results );
})

而且,如果中间的处理函数比较大,不想写在map中,也可以单独写成一个函数,然后传递进去,不过参数传递还是要符合规则的:

var files = ['./file/cnode_1.txt', './file/cnode_2.txt', './file/cnode_3.txt'];

var read = function(file, cb){
 fs.readFile(file, 'utf-8', function(err, data){
  cb(err, data);
 })
}
async.map(files, read, function(err, result){
 console.log( result );
})

这里还有一个mapLimit,可以传递一个参数limit,用来限制并发的数量:mapLimit(coll, limit, iteratee, callbackopt)

// 并发数量为2
async.mapLimit(files, 2, read, function(err, result){
 console.log( result );
})

同时,集合类中还有其他的方法,我们也稍微了解下:

  1. each : 与map类似,但是最后的回调函数里没有results,each只循环不负责处理结果
  2. every : 中间处理函数iteratee的参数(err, boolean)需要传递一个boolean值,若所有选项的结果都为true,则results为true
  3. some : 与every类似,只是只要其中一个选项的结果为true,则results为true
  4. filter : 对coll进行筛选,筛选出结果为true的结果
  5. reject : 与filter正好相反,筛选出结果为false的结果
  6. concat : 将每个异步操作的结果合并为一个数组

2.2 流程控制类

上面的集合类是对一个集合进行相同的处理,集合中的每一项都处理完后,再对结果进行回调处理。而多个回调方法执行时,则需要对这几个回调方法进行控制了。

多个回调方法执行时,通常有这么几个流程:

  1. 串行且无关联,即执行完一个后再依次执行下一个,且相互之间无数据交互,都执行完后,再执行最后的回调函数。可以使用async.series
  2. 串行且有关联,即执行完一个后再依次执行下一个,且上一个回调函数的结果会作为下一个回调函数的参数。可以使用async.waterfall
  3. 并行,这几个回调函数同时并发执行,都执行完成后,再执行最后的回调函数。可以使用async.parallel

当然还有其他更复杂的流程,这里也只聊上面的三种情况。

async.series,async.waterfall和async.parallel的语法都是一样的:
async.Method(coll, function(err, results){

})

其中coll既可以是数组,也可以是json格式的,而且results的类型与coll对应。

串行且无关联async.series

// 串行且无关联,数组格式
async.series([
 function(cb){
  getAllList(function(result){
   cb(null, result);
  });
 },
 function(cb){
  getAllUser(function(result){
   cb(null, result);
  });
 }
], function(err, result){
 console.log(result);
})

同时串行的异步可以是json格式的:

// 串行且无关联,json个数
async.series({
 one: function(cb){
  getAllList(function(result){
   cb(null, result);
  });
 },
 two: function(cb){
  getAllUser(function(result){
   cb(null, result);
  });
 }
}, function(err, result){
 console.log(result);
})

串行且有关联async.waterfall

// 串行且上一个结果作为下一个的参数
async.waterfall([
 function(cb){
  getListById(1, function(result){
   cb(null, result);
  });
 },
 function(params, cb){
  console.log(params);
  getAllUser(function(result){
   cb(null, result);
  });
 }
], function(err, result){
 console.log(result);
})

并行async.parallel

// 并行,getAllList与getAllUser同时执行
async.parallel([
 function(cb){
  getAllList(function(result){
   cb(null, result);
  });
 },
 function(cb){
  getAllUser(function(result){
   cb(null, result);
  });
 }
], function(err, result){
 console.log(result);
})

关于并行的异步操作,这里还有一个async.parallelLimit,限制并发的数量:

// 并发数量为2
async.parallelLimit([
 iteratee1, iteratee2, iteratee3, ...
], 2, function(err, results){
 
})

2.3 工具类

async中也提供了不少的工具方法可供使用,比如async.log可以输出回调方法中的值,第1个参数为函数,后面的参数为传递给函数的参数:

var hello = function(name, callback) {
 setTimeout(function() {
  callback(null, 'hello ' + name);
 }, 1000);
};

// 将'world'传递给hello方法
async.log(hello, 'world'); // 'hello world'

这里面还有apply, dir, timeout等方法。

总结

使用async控制异步流程非常的方便,而且也可以在前端使用,比如可以操作多个ajax请求等。好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
style、 currentStyle、 runtimeStyle区别分析
Aug 01 Javascript
js中的scroll和offset 使用比较的实例与分析
Sep 29 Javascript
浅谈Javascript中深复制
Dec 01 Javascript
JS实现窗口加载时模拟鼠标移动的方法
Jun 03 Javascript
基于jQuery实现网页打印功能
Dec 01 Javascript
jQuery实现简单弹窗遮罩效果
Feb 27 Javascript
vue页面加载闪烁问题的解决方法
Mar 28 Javascript
详解组件库的webpack构建速度优化
Jun 18 Javascript
详解基于iview-ui的导航栏路径(面包屑)配置
Feb 22 Javascript
详解Vue 如何监听Array的变化
Jun 06 Javascript
js实现无限层级树形数据结构(创新算法)
Feb 27 Javascript
vue实现简单跑马灯效果
May 25 Javascript
jQuery基本选择器和层次选择器学习使用
Feb 27 #Javascript
vue2.0数据双向绑定与表单bootstrap+vue组件
Feb 27 #Javascript
flag和jq on 的绑定多个对象和方法(必看)
Feb 27 #Javascript
利用Node.js+Koa框架实现前后端交互的方法
Feb 27 #Javascript
详谈js中数组(array)和对象(object)的区别
Feb 27 #Javascript
JS区分Object与Aarry的六种方法总结
Feb 27 #Javascript
利用Vue.js框架实现火车票查询系统(附源码)
Feb 27 #Javascript
You might like
如何解决PHP使用mysql_query查询超大结果集超内存问题
2016/03/14 PHP
Laravel框架路由和控制器的绑定操作方法
2018/06/12 PHP
PHP如何开启Opcache功能提升程序处理效率
2020/04/27 PHP
ie 处理 gif动画 的onload 事件的一个 bug
2007/04/12 Javascript
Jquery拖拽并简单保存的实现代码
2010/11/28 Javascript
js实现图片旋转的三种方法
2014/04/10 Javascript
jquery+ajax请求且带返回值的代码
2015/08/12 Javascript
JS实现兼容性较好的随屏滚动效果
2015/11/09 Javascript
Bootstrap Paginator分页插件与ajax相结合实现动态无刷新分页效果
2016/05/27 Javascript
老生常谈 关于JavaScript的类的继承
2016/06/24 Javascript
jQuery实现二维码扫描功能
2017/01/09 Javascript
完美实现js选项卡切换效果(一)
2017/03/08 Javascript
Layui table field初始化加载时进行隐藏的方法
2019/09/19 Javascript
JavaScript数组及常见操作方法小结
2019/11/13 Javascript
基于jQuery实现可编辑的表格
2019/12/11 jQuery
vue 实现根据data中的属性值来设置不同的样式
2020/08/04 Javascript
Python连接mssql数据库编码问题解决方法
2015/01/01 Python
简单介绍Python中的round()方法
2015/05/15 Python
Python按行读取文件的简单实现方法
2016/06/22 Python
Python基于递归实现电话号码映射功能示例
2018/04/13 Python
解决Tensorflow安装成功,但在导入时报错的问题
2018/06/13 Python
Flask框架配置与调试操作示例
2018/07/23 Python
python3+requests接口自动化session操作方法
2018/10/13 Python
PyQt5实现简易电子词典
2019/06/25 Python
Python文件操作中进行字符串替换的方法(保存到新文件/当前文件)
2019/06/28 Python
python实现两张图片拼接为一张图片并保存
2019/07/16 Python
Django错误:TypeError at / 'bool' object is not callable解决
2019/08/16 Python
Python3合并两个有序数组代码实例
2020/08/11 Python
欧洲著名的二手奢侈品网站:Vestiaire Collective
2020/03/07 全球购物
JAVA和C++的区别
2013/10/06 面试题
const char*, char const*, char*const的区别是什么
2014/07/09 面试题
师范毕业生个人求职信
2013/12/09 职场文书
民主生活会剖析材料
2014/09/30 职场文书
助学金感谢信
2015/01/20 职场文书
迎新生欢迎词2015
2015/07/16 职场文书
Python中for后接else的语法使用
2021/05/18 Python