从零学习node.js之详解异步控制工具async(八)
分类:计算机教程

前言

聊聊并发与并行

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

并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。

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

并发我们经常提及之,不管是web server,app并发无处不在,操作系统中,指一个时间段中几个程序处于已经启动运行到完毕之间,且这几个程序都是在同一处理机上运行,并且任一个时间点只有一个程序在处理机上运行。很多网站都有并发连接数量的限制,所以当请求发送太快的时候会导致返回值为空或报错。更有甚者,有些网站可能因为你发出的并发连接数量过多而当你是在恶意请求,封掉你的ip。

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

相对于并发,并行可能陌生了不少,并行指一组程序按独立异步的速度执行,不等于时间上的重叠(同一个时刻发生),通过增加cpu核心来实现多个程序(任务)的同时进行。没错,并行做到了多任务的同时进行

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

使用enterproxy控制并发数量

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

enterproxy是朴灵大大为主要贡献的工具,带来一种事件式编程的思维变化,利用事件机制解耦复杂业务逻辑,解决了回调函数耦合性的诟病,将串行等待变成并行等待,提升多异步协作场景下的执行效率

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();

我们如何使用enterproxy控制并发数量?通常如果我们不使用enterproxy和自制的计数器,我们如果抓取三个源:

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

这种深层嵌套,串行的方式

介绍

 var render = function (template, data) {
 _.template(template, data);
 };
$.get("template", function (template) {
 // something
 $.get("data", function (data) {
 // something
 $.get("l10n", function (l10n) {
 // something
 render(template, data, l10n);
 });
 });
});

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

除去这种过去深层嵌套的方法,我们常规的写法的自己维护一个计数器

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

(function(){
 var count = 0;
 var result = {};

 $.get('template',function(data){
 result.data1 = data;
 count  ;
 handle();
 })
 $.get('data',function(data){
 result.data2 = data;
 count  ;
 handle();
 })
 $.get('l10n',function(data){
 result.data3 = data;
 count  ;
 handle();
 })

 function handle(){
 if(count === 3){
  var html = fuck(result.data1,result.data2,result.data3);
  render(html);
 }
 }
})();
  • 集合类(Collections)
  • 流程控制类(Control Flow)
  • 工具类(Utils)

在这里,enterproxy就可以起到这个计数器的作用,它帮你管理这些异步操作是否完成,完成之后,他会自动调用你提供的处理函数,并将抓取到数据当做参数传递过来

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

var ep = new enterproxy();
ep.all('data_event1','data_event2','data_event3',function(data1,data2,data3){
 var html = fuck(data1,data2,data3);
 render(html);
})

$.get('http:example1',function(data){
 ep.emit('data_event1',data);
})

$.get('http:example2',function(data){
 ep.emit('data_event2',data);
})

$.get('http:example3',function(data){
 ep.emit('data_event3',data);
})

二、函数介绍

enterproxy还提供了其他不少场景所需的API,可以自行学习下这个API enterproxy

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

使用async控制并发数量

2.1 集合类

假如我们有40个请求需要发出,很多网站可能会因为你发出的并发连接数太多而当你是在恶意请求,把你的IP封掉。
所以我们总是需要控制并发数量,然后慢慢抓取完这40个链接。

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

使用async中mapLimit控制一次性并发数量为5,一次性只抓取5个链接。

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

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

我们首先应该知道什么是并发,为什么需要限制并发数量,都有哪些处理方案。然后就可以去文档具体看一下API如何使用。async文档可以很好的学习这些语法。

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

模拟一组数据,这里返回的数据是假的,返回的延时是随机的。

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

var concurreyCount = 0;
var fetchUrl = function(url,callback){
 // delay 的值在 2000 以内,是个随机的整数 模拟延时
 var delay = parseInt((Math.random()* 10000000) % 2000,10);
 concurreyCount  ;
 console.log('现在并发数是 ' , concurreyCount , ' 正在抓取的是' , url , ' 耗时'   delay   '毫秒');
 setTimeout(function(){
 concurreyCount--;
 callback(null,url   ' html content');
 },delay);
}

var urls = [];
for(var i = 0;i<30;i  ){
 urls.push('http://datasource_'   i)
}
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 );
})

然后我们使用async.mapLimit来并发抓取,并获取结果。

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

async.mapLimit(urls,5,function(url,callback){
 fetchUrl(url,callbcak);
},function(err,result){
 console.log('result: ');
 console.log(result);
})
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 );
})

模拟摘自alsotang

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

运行输出后得到以下结果

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

图片 1

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

我们发现,并发数从1开始增长,但是增长到5时,就不在增加。然有任务时就继续抓取,并发连接数量始终控制在5个。

  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 : 将每个异步操作的结果合并为一个数组

完成node简易爬虫系统

本文由美洲杯赔率发布于计算机教程,转载请注明出处:从零学习node.js之详解异步控制工具async(八)

上一篇:PHP中的函数-- foreach()的用法详解男篮世界杯赔率 下一篇:没有了
猜你喜欢
热门排行
精彩图文
  • 详解Vue.js入门环境搭建
    详解Vue.js入门环境搭建
    vue这个新的工具,确实能够提高效率,在经历的一段时间的摧残之后,终于能够有一个系统的认识了,下面就今天的收获做一个总结,也是vue入门的精髓:
  • javascript 中的继承实例详解,javascript实例男篮世
    javascript 中的继承实例详解,javascript实例男篮世
    javascript 中的继承实例详解 javascript 中的继承实例详解,javascript实例 javascript 中的继承实例详解 阅读目录 原型链继承 借用构造函数 组合继承 寄生组合式
  • 剖析 【美洲杯赔率】rudy 访问控制
    剖析 【美洲杯赔率】rudy 访问控制
     前面,我们说Ruby没有函数,只有方法.而且实际上有不止一种方法.这一节我们介绍访问控制(access controls).   局部变量由小写字母或下划线(_)开头.局部变量
  • ruby 存取器 概念
    ruby 存取器 概念
     前面一节的 Fruit 类有两个实变量,分别表述水果的类型和状态.直到为这个类写了一个定制的inspect方法,我们方才了解它不会对一个缺乏属性的水果做出合
  • thinkphp美洲杯赔率: 内置字符串截取函数
    thinkphp美洲杯赔率: 内置字符串截取函数
    在thinkphp模板引擎里可以这样写:{$vo.title|msubstr=0,5,’utf-8′,false} thinkphp内置了一个可以媲美smarty的模板引擎,给我们带来了很大的方便。调用函数也一样,