为什么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 限制并发数量相关推荐

  1. node和php处理高并发,node.js“多线程”如何处理高并发任务?,nodejs java 高并发

    node.js"多线程"如何处理高并发任务?node . js"多线程"是如何处理高度并发的任务的?,下面的文章介绍了使用nodejs"多线程&quo ...

  2. 解秘 Node.js 单线程实现高并发请求原理,以及串联同步执行并发请求的方案

    最近在做一个支持多进程请求的 Node 服务,要支持多并发请求,而且请求要按先后顺序串联同步执行返回结果. 对,这需求就是这么奇琶,业务场景也是那么奇琶. 需求是完成了,为了对 Node.js 高并发 ...

  3. Node.js:浅析高并发与分布式集群

    Node特性:高并发 在解释node为什么能够做到高并发之前,不妨先了解一下node的其他几个特性: 单线程 我们先来明确一个概念,即:node是单线程的,这一点与JavaScript在浏览器中的特性 ...

  4. node.js Promise详解(尚硅谷李立超老师视频笔记)

    • Promise就是一个用来存储数据对象,但是由于Promise存取的方式的特殊,所以可以直接将异步调用的结果存储到Promise中 Promise存储异步数据: function sum(a,b) ...

  5. node.js Promise简单介绍

    转自百度: https://baijiahao.baidu.com/s?id=1589455136001194151&wfr=spider&for=pc

  6. [node.js] Promise中,resolve, reject 之后需不需要 return?

    答案是,需要. 为了阻止后面的代码被执行,应当加上 return,或者直接使用: return resolve(...) 详细解释:https://stackoverflow.com/a/325360 ...

  7. node.js基于WebStorm服装购物网站的设计与实现毕业设计源码281444

    摘  要 随着社会的发展,计算机的优势和普及使得服装购物网站的开发成为必需.服装购物网站主要是借助计算机,通过对首页.站点管理(轮播图.公告栏)用户管理(管理员.普通用户)内容管理(交流论坛.论坛分类 ...

  8. node.js基于微信小程序的校园失物招领毕业设计源码072343

    微信小程序的校园失物招领系统 摘  要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,微信小程序的校 ...

  9. node.js+小程序基于微信小程序的校园失物招领系统毕业设计源码072343

    微信小程序的校园失物招领系统 摘  要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,微信小程序的校 ...

最新文章

  1. python的六大数据类型中可以改变的数据类型为_Python中数据类型转换
  2. CA双向认证的时候,如果一开始下载的证书就有问题的,怎么保证以后的交易没有问题?...
  3. vue - v-if 注意点
  4. springboot 整合druid
  5. 实验吧web-中-忘记密码了
  6. 使用entityframework操作sqlite数据库
  7. 链表的实现(Java语言描述)
  8. python基本网络爬虫代码_Python实现网页爬虫基本实现代码解读
  9. TP-link与电力线通信(智能电网)
  10. 计算机接口技术相关设计,计算机接口技术的特点与应用设计研究
  11. 右上角的引用文献格式_论文标注参考文献格式
  12. 微信公众号菜单模板设置
  13. 估计π的第一种方法:蒲丰(buffon)投针
  14. WIN7,让光驱走开
  15. 2021 中国科学技术大学 大数据学院 推免夏令营 经历
  16. java模拟转账_事务-模拟银行用户转账
  17. SDUT数据结构实验之链表一:顺序建立链表
  18. 【08月28日】A股滚动市盈率PE历史新低排名
  19. 圆形路标_自定义工具:路标不足时
  20. 戏言产品三板斧,拿着三宝来作秀

热门文章

  1. checked js 获取值_js获取所有checkbox的值的简单实例
  2. python自带的url提取器
  3. matlab 调用opencv,matlab调用opencv (mac 或 linux)
  4. python中如何将数字改成字符串_python中如何将数字转字符串
  5. gitlub统计一个人提交代码行数
  6. spring的单例回收
  7. Windows服务器上Mqtt服务器EMQX的安装使用
  8. ElementUI中el-upload中怎样限制上传文件的格式
  9. Windows服务器上怎样开放指定端口
  10. Sqlserver2014怎样配置远程连接