15 行代码实现并发控制(javascript)
前言
做过爬虫的都知道,要控制爬虫的请求并发量,其实也就是控制其爬取频率,以免被封IP,还有的就是以此来控制爬虫应用运行内存,否则一下子处理N个请求,内存分分钟会爆。
而 python爬虫一般用多线程来控制并发,
然而如果是node.js爬虫,由于其单线程无阻塞性质以及事件循环机制,一般不用多线程来控制并发(当然node.js也可以实现多线程,此处非重点不再多讲),而是更加简便地直接在代码层级上实现并发。
为图方便,开发者在开发node爬虫一般会找一个并发控制的npm包,然而其实实现一个并发控制的函数也并不难。
具体实现
参数
首先,一个基本的并发控制函数,基本要有以下3个参数:
- list {Array} – 要迭代的数组
- limit {number} – 控制的并发数量
- asyncHandle {function} – 对list的每一个项的处理函数
设计
以下以爬虫为实例进行讲解
设计思路其实很简单,假如并发量控制是 5
1.首先,瞬发 5 个异步请求,我们就得到了并发的 5 个异步请求
// limit = 5 while(limit--) { handleFunction(list) }
2.然后,等每个异步请求执行完,执行下一个list项
let recursion = (arr) => { return asyncHandle(arr.shift()) .then(()=>{ // 迭代数组长度不为0, 递归执行自身 if (arr.length!==0) return recursion(arr) // 迭代数组长度为0,结束 else return 'finish'; }) }
3.等list所有的项迭代完之后的回调
return Promise.all(allHandle)
代码
上述步骤组合起来,就是
/** * @params list {Array} - 要迭代的数组 * @params limit {Number} - 并发数量控制数 * @params asyncHandle {Function} - 对`list`的每一个项的处理函数,参数为当前处理项,必须 return 一个Promise来确定是否继续进行迭代 * @return {Promise} - 返回一个 Promise 值来确认所有数据是否迭代完成 */ let mapLimit = (list, limit, asyncHandle) => { let recursion = (arr) => { return asyncHandle(arr.shift()) .then(()=>{ if (arr.length!==0) return recursion(arr) // 数组还未迭代完,递归继续进行迭代 else return 'finish'; }) }; let listCopy = [].concat(list); let asyncList = []; // 正在进行的所有并发异步操作 while(limit--) { asyncList.push( recursion(listCopy) ); } return Promise.all(asyncList); // 所有并发异步操作都完成后,本次并发控制迭代完成 }
测试demo
模拟一下异步的并发情况
var dataLists = [1,2,3,4,5,6,7,8,9,11,100,123]; var count = 0; mapLimit(dataLists, 3, (curItem)=>{ return new Promise(resolve => { count++ setTimeout(()=>{ console.log(curItem, '当前并发量:', count--) resolve(); }, Math.random() * 5000) }); }).then(response => { console.log('finish', response) })
结果如下:
手动抛出异常中断并发函数测试:
var dataLists = [1,2,3,4,5,6,7,8,9,11,100,123]; var count = 0; mapLimit(dataLists, 3, (curItem)=>{ return new Promise((resolve, reject) => { count++ setTimeout(()=>{ console.log(curItem, '当前并发量:', count--) if(curItem > 4) reject('error happen') resolve(); }, Math.random() * 5000) }); }).then(response => { console.log('finish', response) })
并发控制情况下,迭代到5,6,7 手动抛出异常,停止后续迭代:
相关推荐
-
前端必知必会HTTP请求系列(三)HTTP报文内的http信息 javascript/jquery
2019-6-1
-
web开发的跨域问题详解 javascript/jquery
2019-8-20
-
IOS 浏览器页面布局错位(如:点不到)的分析与解决 javascript/jquery
2019-11-1
-
PixiJS基础教程 javascript/jquery
2020-5-23
-
实现平滑过渡的拖拽排序 javascript/jquery
2019-6-15
-
2020年面向前端开发人员的10个很棒的 JS 库 javascript/jquery
2020-5-28
-
docsify – 生成文档网站简单使用教程 javascript/jquery
2019-1-1
-
Js中this的用法 javascript/jquery
2017-12-18
-
ECMAScript6-let与const命令详解 javascript/jquery
2019-6-10
-
在原生JavaScript中创建不可变对象 javascript/jquery
2020-5-18