媒介
首发于 github blog
做过爬虫的都晓得,要掌握爬虫的要求并发量,实在也就是掌握其爬取频次,以避免被封IP,另有的就是以此来掌握爬虫运用运转内存,不然一会儿处置惩罚N个要求,内存分分钟会爆。
而 python
爬虫平常用多线程来掌握并发,
但是如果是node.js
爬虫,因为其单线程无壅塞性子以及事宜轮回机制,平常不必多线程来掌握并发(固然node.js
也能够完成多线程,此处非重点不再多讲),而是越发轻便地直接在代码层级上完成并发。
为图轻易,开辟者在开辟node
爬虫平常会找一个并发掌握的npm包
,但是第三方的模块有时候也并不能完整满足我们的特别需求,这时候我们能够就需要一个本身定制版的并发掌握函数。
下面我们用15行代码完成一个并发掌握的函数。
详细完成
参数
起首,一个基础的并发掌握函数,基础要有以下3个参数:
-
list
{Array} – 要迭代的数组 -
limit
{number} – 掌握的并发数目 -
asyncHandle
{function} – 对list
的每个项的处置惩罚函数
设想
以下以爬虫为实例举行解说
设想思绪实在很简单,如果并发量掌握是 5
起首,瞬发 5 个异步要求,我们就得到了并发的 5 个异步要求
// limit = 5 while(limit--) { handleFunction(list) }
然后,这 5 个异步要求中不管哪个先实行完,都邑继承实行下一个
list
项let recursion = (arr) => { return asyncHandle(arr.shift()) .then(()=>{ // 迭代数组长度不为0, 递归实行本身 if (arr.length!==0) return recursion(arr) // 迭代数组长度为0,完毕 else return 'finish'; }) }
等
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 手动抛出非常,住手后续迭代: