Nodejs - 如何用 eventproxy 模块控制并发
本文目标
本文的目标是获取 ZOJ 1001-1010 每道题 best solution 的作者 id,取得数据后一次性输出在控制台。
前文 如何用 Nodejs 分析一个简单页面 我们讲了如何用 Nodejs 简单地对一个页面进行分析,我们再来理一理,温故而知新。首先,我们的目标是能输出在页面上,这时我们就需要 http
模块,或者封装了 http
模块的 express
模块。其次我们需要获取博客园首页的页面代码,就要发送 http 请求,而 superagent
模块正是我们所需要的。最后我们要对获取的页面代码进行分析,cheerio
模块能让我们用类似 jQuery 的语法对页面进行解析,何止一个爽字了得!
实践
再看今天我们的任务,粗看之下,少了输出在页面这个条件(不需要用 http 甚至 express 模块了),而需要输出每道题的 best solution 的作者 id,似乎并不是很难,我们来分析下 1001 这道题的提交统计页面,很显然第一条就是最佳 solution 嘛,我们像前面一样用 SuperAgent
模块请求该页面,然后用 cheerio
模块进行解析,不就大功告成了?!什么,要解析 10 个页面?那就发送 10 次 http 请求喽!
我们再来看下这个页面的 url http://acm.zju.edu.cn/onlinejudge/showProblemStatus.do?problemId=1,简直不能太爽有木有,那么 1002 题就是 problemId=2
吧,以此类推。
我们很快写下了如下代码:
var cheerio = require('cheerio');
var superagent = require('superagent');
var urls = [];
for (var i = 1; i <= 10; i++) {
var tmp = 'http://acm.zju.edu.cn/onlinejudge/showProblemStatus.do?problemId=' + i;
urls.push(tmp);
}
urls.forEach(function(item) {
superagent
.get(item)
.end(function (err, sres) { // callback
if (err) {
console.error(err);
}
var $ = cheerio.load(sres.text);
var ans = $('.runUserName a font').eq(0).text();
console.log(ans);
});
});
控制台输出了我们想要的 10 个 id 有木有!可是...这跟前面学的好像并没有什么区别,无非是多做了 几次请求?我们再来看下本文目标,一次性输出有没有!再看上面代码的执行,很显然是一个一个输出,所以有时候审题真的很重要。那么,我们定义一个数组,然后把每次的结果存入数组,最后再将数组输出,不就行了?可是会输出一个空数组,是啊,superagent
是异步执行的啊亲!
eventproxy 模块
这种事情,早就有人想到了,eventproxy
模块就可以完美解决这个问题。我们想想如果自己写该怎么做?
SuperAgent 其实可以看做是服务端的 ajax,如果我们要在客户端完成 2 次 ajax操作,然后将返回的信息一起输出,会怎么做?
可能会这么写:
// jQuery
$.get(url1, function(data1) {
$.get(url2, function(data2) {
var ans = gao(data1, data2);
console.log(ans);
})
});
但是如果是 10 次请求呢?那就要嵌套 10 个回调了,太乱了。而且这样写效率太低,跟同步获取是一样一样的,果断 pass!
通常的做法是维护一个计数器,每次异步处理完后,将该计数器自加一,如果累计到了一个特定的数值(比如 2 次请求那么就是 2),就触发事件处理。
var count = 0
, datas = [];
function gao() {
if (count !== 2)
return;
// ...
}
// jQuery
$.get(url1, function(data) {
datas.push(data);
count++;
gao();
});
$.get(url2, function(data) {
datas.push(data);
count++;
gao();
});
但是这样写,总觉得不够优雅,最后我们来学习下使用 eventproxy
模块后的写法。eventproxy 实际上就起到了计数器的作用,它来帮你管理到底这些异步操作是否完成,完成之后,它会自动调用你提供的处理函数,并将抓取到的数据当参数传过来。
var ep = new eventproxy();
ep.all('data1_event', 'data2_event', function (data1, data2) {
var ans = gao(data1, data2,);
console.log(ans);
});
$.get(url1, function (data) {
ep.emit('data1_event', data);
});
$.get(url2, function (data) {
ep.emit('data2_event', data);
});
ep.all() 监听了两个事件,data1_event 以及 data2_event,每次当一个数据源抓取完成后,就通过 ep.emit() 告诉 ep 自己,我的抓取已经完成了,相当于维护了一个计数器。当 2 件事件都完成时,就会调用 ep.all() 的回调函数对数据进行处理。
eventproxy 提供了不少其他场景所需的 API,但最最常用的用法就是以上的这种,即:
- 先 var ep = new eventproxy(); 得到一个 eventproxy 实例。
- 告诉它你要监听哪些事件,并给它一个回调函数。ep.all('event1', 'event2', function (result1, result2) {})。
- 在适当的时候 ep.emit('event_name', eventData)。
如果是重复执行多次,比如读取10个文件,调用5次数据库等,可以用到它的 after
方法,api 可以参考重复异步协作,直接看代码:
// 得到一个 eventproxy 的实例
var ep = new eventproxy();
// 命令 ep 重复监听 urls.length 次(在这里也就是 10 次) `topic_html` 事件再行动
ep.after('topic_html', urls.length, function (topics) {
// topics 是个数组,包含了 10 次 ep.emit('topic_html', page) 中的那 10 个 page
// 开始行动
topics = topics.map(function(page) {
// 接下来都是 jquery 的用法了
var $ = cheerio.load(page);
var userId = $('.runUserName a font').eq(0).text();
return userId;
});
console.log(topics);
});
urls.forEach(function(item) {
superagent.get(item)
.end(function (err, res) {
ep.emit('topic_html', res.text);
});
});
其实 emit() 和 after() 可以这样想象,一个负责抛出事件,一个负责监听事件(当然是先监听后抛出),余下的也就不难理解了。关于 eventproxy 更多可以参考 JacksonTian/eventproxy
总结
今天介绍的 eventproxy
模块是控制并发用的,有时我们需要同时发送 N 个 http 请求,然后利用得到的数据进行后期的处理工作,如何方便地判断数据已经全部并发获取得到,就可以用到该模块了。而模块不仅可以在服务端使用,也可以应用在客户端,详细可以参考JacksonTian/eventproxy
Nodejs - 如何用 eventproxy 模块控制并发的更多相关文章
- nodejs高并发大流量的设计实现,控制并发的三种方法
nodejs高并发大流量的设计实现,控制并发的三种方法eventproxy.async.mapLimit.async.queue控制并发Node.js是建立在Google V8 JavaScript引 ...
- 手把手教你学node.js 之使用 eventproxy 控制并发
使用 eventproxy 控制并发 目标 建立一个 lesson4 项目,在其中编写代码. 代码的入口是 app.js,当调用 node app.js 时,它会输出 CNode(https://cn ...
- async和enterproxy控制并发数量
聊聊并发与并行 并发我们经常提及之,不管是web server,app并发无处不在,操作系统中,指一个时间段中几个程序处于已经启动运行到完毕之间,且这几个程序都是在同一处理机上运行,并且任一个时间点只 ...
- selenium模块控制浏览器
利用selenium模块控制浏览器 导入selenium模块:from selenium import webdriver browserFirefox = webdriver.Firefox()#打 ...
- [并发编程 - socketserver模块实现并发、[进程查看父子进程pid、僵尸进程、孤儿进程、守护进程、互斥锁、队列、生产者消费者模型]
[并发编程 - socketserver模块实现并发.[进程查看父子进程pid.僵尸进程.孤儿进程.守护进程.互斥锁.队列.生产者消费者模型] socketserver模块实现并发 基于tcp的套接字 ...
- java并发编程学习:用 Semaphore (信号量)控制并发资源
并发编程这方面以前关注得比较少,恶补一下,推荐一个好的网站:并发编程网 - ifeve.com,上面全是各种大牛原创或编译的并发编程文章. 今天先来学习Semaphore(信号量),字面上看,根本不知 ...
- 大熊君大话NodeJS之------Connect中间件模块(第一季)
一,开篇分析 截止到今天来说,NodeJS系列文章已经有将近十篇了,让我们回顾一下: (1),大熊君大话NodeJS之开篇------Why NodeJS(将Javascript进行到底) (2),大 ...
- Java--Semaphore控制并发线程数量
package com; import java.util.concurrent.Semaphore; /** * Created by yangyu on 16/11/28. */ /** * Se ...
- entity framework如何控制并发
entity framework如何控制并发 针对字段http://msdn.microsoft.com/en-us/library/vstudio/bb738618(v=vs.100).aspx ...
随机推荐
- INITIAL参数设置导致TRUNCATE TABLE不能降低高水位线案例
在一个数据库使用下面SQL找出了一批需要降低高水位线的表,其中有几个表没有数据,于是我打算用TRUNCATE来降低高水位线HWM SELECT a.owner, a.segment_na ...
- RedHat Linux RHEL6配置本地YUM源
YUM是Yellow dog Updater Modified的简称,起初是由yellow dog这一发行版的开发者Terra Soft研发,用python写成,那时还叫做yup(yellow dog ...
- native2ascii 使用说明
native2ascii.exe 是Java的一个文件转码工具,是将特殊各异的内容转为用指定的编码标准文体形式统一的表现出来,它通常位于JDK_home\bin目录下,安装好Java SE后,可在命令 ...
- 使用 Eclipse 玩转 C、C++
因为做Java开发,所以习惯了使用Eclipse.现在需要写C++程序,真心不想用VS那样的重量级的IDE,VC++6.0又是那么的不友好.使用一款自己熟悉的IDE,工作起来就顺手.为了可以在Ecli ...
- 《java JDK7 学习笔记》之异常处理
1.java中所有的错误都会被打包为对象,JVM会尝试执行try区块中的程序代码,如果发生错误,执行流程会跳离错误发生点,然后比较catch括号中声明的异常类型,是否符合被抛出的错误对象类型,如果是的 ...
- [Java入门笔记] Java语言基础(五):数组
简介 数组可用用于存储存储多个数据,Java的数组要求所有的数组元素具有一种相同的数据类型.一旦数组初始化完成,数组在内存中的空间被固定下来,长度不可改变,即使把数组的元素清空,所占用的空间依然被保留 ...
- jQuery 3.0的buildFragment
在 jQuery3.0中,buildFragment 是一个私有函数,用来构建一个包含子节点 fragment 对象.这个 fragment 在 DOM1 中就已经有了,所有浏览器都支持.当频繁操作( ...
- Apache 安装配置详情
本次文章讲解Apache的安装和基本的配置 输入PHP环境搭建的一部分 PHP完整配置信息请参考 http://www.cnblogs.com/azhe-style/p/php_new_env_bui ...
- Android APP 两种用程序拨号的方式
想在APP中添加一个拨号功能该怎样做呢?Android提供了两种方式,一种是ACTION_CALL方式直接拨打,另一种是ACTION_DIAL方式打开系统的拨号界面. 下面我们来做个小例子 首先需要在 ...
- Leetcode: word search
July 6, 2015 Problem statement: Word Search Given a 2D board and a word, find if the word exists in ...