Node.js Promise.all 限制并发数量
为什么80%的码农都做不了架构师?>>>
Promise.all 本身不负责执行,执行过程在传递给Promise.all之前已经开始,Promise.all只等待全部执行完成,执行resolve,或碰到有执行失败,立即执行reject部分。Promise.all非常好用,唯一的问题是,不能限制并发数量,所有任务同时开始执行,因为Promise.all本身不负责执行具体任务,所以也无法实现并发控制。
实现一个简单的可以控制并发数量的Promise.allLimit函数,可以通过参数来控制并发数量。代码:
/* promise-limit.js */
/* jshint esversion: 6 */
/*jslint node: true */
Promise.allLimit = function(arr, wrap, limit, callback) {return new Promise((resolve, reject) => {var total = arr.length;var result = new Array(total);var rejected = false;var dones = 0;function run(n) {setTimeout(() => {wrap(n, arr.shift()).then(res => {return typeof callback === 'function' ? callback(n, res) : Promise.resolve(res);}).then(res => {dones++;result[n] = res;if (!rejected) {if (arr.length) {run(total - arr.length);} else if (dones === total) {resolve(result);}}}).catch(err => {rejected = true;reject(err);});}, 0);}arr.slice(0, limit).forEach((v, n) => {run(n);});});
};
同样返回一个Promise对象,可以直接替换Promise.all,不同的是,需要传递一个函数(wrap参数),用来包裹生成每一个具体执行的Promise对象,limit用来限定并发数量,在指定并发任务内,一个任务完成后,再吸入一个新任务继续执行。
callback用来解析每一次任务完成后所需要的后续动作,比如存储下载的内容、或将参数做变换,必须也返回一个Promise对象。
测试代码:
Promise.allLimit([2000, 1500, 2500, 3000, 1500], function(n, time) {return new Promise((resolve, reject) => {console.log("Start Job: ", n, time);// setTimeout(2500 === time ? reject : resolve, time, "Time: " + time); // 测试rejectsetTimeout(resolve, time, "Time: " + time);});
}, 2, (n, res) => {// log Job n doneconsole.log("Done Job: ", res);return Promise.resolve(n);
// log Job n done
}).then(result => {console.log("All Done: ", result);
}).catch(err => {console.log("Error: ", err);
});
最后贡献一个下载妹子图的简单代码,默认控制并发数量10个。
#!/usr/bin/env node/* jshint esversion: 6 */
/*jslint node: true */
require('./promise-limit.js');const FS = require('fs');
const PATH = require('path');
const UTIL = require('util');
const ARGV = require('yargs').argv;
const REQUEST = require('request');
const CHEERIO = require('cheerio');if (!ARGV.url || !ARGV.dir || !/\/$/.test(ARGV.dir) || !ARGV.img || !ARGV.total || !ARGV.from || !ARGV.to) {console.log("usage: --url http://example.com --dir ./imgs/ --img '.main-image img' --total '共(\d+)页' --from '.php' --to '_%d.php' ");process.exit();
}const parallel = ARGV.parallel || 10; //并发数量const headers = {'Referer': ARGV.url,'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Accept-Encoding': 'gzip, deflate, sdch','Accept-Language': 'en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4','User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36'
};function fetch(url, encoding = 'utf8') {return new Promise((resolve, reject) => {REQUEST({url: url,headers: headers,gzip: true,encoding: encoding}, (error, response, body) => {if (error) {reject(error);} else {resolve(body);}});});
}function write(file, content) {return new Promise(function(resolve, reject) {FS.writeFile(file, content, function(err) {if (err) {reject(err);} else {resolve();}});});
}fetch(ARGV.url).then(rsp => {const total = parseInt((rsp.match(new RegExp(ARGV.total)) || [0, 0])[1]);if (!total) {throw new Error('Match total error');}console.log("Total: %d, Parallel: %d", total, parallel);return Promise.allLimit(Array.from({length: total}, (v, k) => k + 1), (k, v) => {return fetch(v === 1 ? ARGV.url : ARGV.url.replace(ARGV.from, UTIL.format(ARGV.to, v)));}, parallel, (k, res) => {let src = CHEERIO.load(res)(ARGV.img).attr('src');let file = (k + 1) + PATH.extname(src);return fetch(src, null).then(img => {return write(ARGV.dir + file, img);}).then(() => {console.log("OK: [" + file + "]\t" + src);return file;}).catch(err => {console.log("ER: [" + file + "]\t" + src + " : " + err.toString());});});}).then(rsp => {console.log("All Jobs Done: ");console.log(rsp);}).catch(err => {console.log("Fetch failed: %s", err.toString());});
执行:
./request.js --total '(\d+)</span></a><a[^<>]+><span>下一页' --from '/71636' --to '/71636/%d' --img '.main-image img' --dir ./71636/ --url 'http://www.mzitu.com/71636' --parallel 10
转载于:https://my.oschina.net/jsk/blog/789877
Node.js Promise.all 限制并发数量相关推荐
- node和php处理高并发,node.js“多线程”如何处理高并发任务?,nodejs java 高并发
node.js"多线程"如何处理高并发任务?node . js"多线程"是如何处理高度并发的任务的?,下面的文章介绍了使用nodejs"多线程&quo ...
- 解秘 Node.js 单线程实现高并发请求原理,以及串联同步执行并发请求的方案
最近在做一个支持多进程请求的 Node 服务,要支持多并发请求,而且请求要按先后顺序串联同步执行返回结果. 对,这需求就是这么奇琶,业务场景也是那么奇琶. 需求是完成了,为了对 Node.js 高并发 ...
- Node.js:浅析高并发与分布式集群
Node特性:高并发 在解释node为什么能够做到高并发之前,不妨先了解一下node的其他几个特性: 单线程 我们先来明确一个概念,即:node是单线程的,这一点与JavaScript在浏览器中的特性 ...
- node.js Promise详解(尚硅谷李立超老师视频笔记)
• Promise就是一个用来存储数据对象,但是由于Promise存取的方式的特殊,所以可以直接将异步调用的结果存储到Promise中 Promise存储异步数据: function sum(a,b) ...
- node.js Promise简单介绍
转自百度: https://baijiahao.baidu.com/s?id=1589455136001194151&wfr=spider&for=pc
- [node.js] Promise中,resolve, reject 之后需不需要 return?
答案是,需要. 为了阻止后面的代码被执行,应当加上 return,或者直接使用: return resolve(...) 详细解释:https://stackoverflow.com/a/325360 ...
- node.js基于WebStorm服装购物网站的设计与实现毕业设计源码281444
摘 要 随着社会的发展,计算机的优势和普及使得服装购物网站的开发成为必需.服装购物网站主要是借助计算机,通过对首页.站点管理(轮播图.公告栏)用户管理(管理员.普通用户)内容管理(交流论坛.论坛分类 ...
- node.js基于微信小程序的校园失物招领毕业设计源码072343
微信小程序的校园失物招领系统 摘 要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,微信小程序的校 ...
- node.js+小程序基于微信小程序的校园失物招领系统毕业设计源码072343
微信小程序的校园失物招领系统 摘 要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,微信小程序的校 ...
最新文章
- python的六大数据类型中可以改变的数据类型为_Python中数据类型转换
- CA双向认证的时候,如果一开始下载的证书就有问题的,怎么保证以后的交易没有问题?...
- vue - v-if 注意点
- springboot 整合druid
- 实验吧web-中-忘记密码了
- 使用entityframework操作sqlite数据库
- 链表的实现(Java语言描述)
- python基本网络爬虫代码_Python实现网页爬虫基本实现代码解读
- TP-link与电力线通信(智能电网)
- 计算机接口技术相关设计,计算机接口技术的特点与应用设计研究
- 右上角的引用文献格式_论文标注参考文献格式
- 微信公众号菜单模板设置
- 估计π的第一种方法:蒲丰(buffon)投针
- WIN7,让光驱走开
- 2021 中国科学技术大学 大数据学院 推免夏令营 经历
- java模拟转账_事务-模拟银行用户转账
- SDUT数据结构实验之链表一:顺序建立链表
- 【08月28日】A股滚动市盈率PE历史新低排名
- 圆形路标_自定义工具:路标不足时
- 戏言产品三板斧,拿着三宝来作秀