摘要:而爬虫一般用多线程来控制并发,然而如果是爬虫,由于其单线程无阻塞性质以及事件循环机制,一般不用多线程来控制并发当然也可以实现多线程,此处非重点不再多讲,而是更加简便地直接在代码层级上实现并发。下面我们用行代码实现一个并发控制的函数。
前言
首发于 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 手动抛出异常,停止后续迭代:
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/107238.html
摘要:编写异步代码可能是一种不同的体验,尤其是对异步控制流而言。回调函数的准则在编写异步代码时,要记住的第一个规则是在定义回调时不要滥用闭包。为回调创建命名函数,避免使用闭包,并将中间结果作为参数传递。 本系列文章为《Node.js Design Patterns Second Edition》的原文翻译和读书笔记,在GitHub连载更新,同步翻译版链接。 欢迎关注我的专栏,之后的博文将在专...
摘要:表级锁表级锁表级别的锁定是各存储引擎中最大颗粒度的锁定机制。当前没有其他事务持有表中任意一行的排他锁。为了检测是否满足第二个条件,事务必须在确保表不存在任何排他锁的前提下,去检测表中的每一行是否存在排他锁。一、表级锁、行级锁、页级锁数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则。MySQL数据库由于其自身架构的特点,存在多种数据存...
摘要:感谢大神的免费的计算机编程类中文书籍收录并推荐地址,以后在仓库里更新地址,声音版全文狼叔如何正确的学习简介现在,越来越多的科技公司和开发者开始使用开发各种应用。 说明 2017-12-14 我发了一篇文章《没用过Node.js,就别瞎逼逼》是因为有人在知乎上黑Node.js。那篇文章的反响还是相当不错的,甚至连著名的hax贺老都很认同,下班时读那篇文章,竟然坐车的还坐过站了。大家可以很...
摘要:感谢大神的免费的计算机编程类中文书籍收录并推荐地址,以后在仓库里更新地址,声音版全文狼叔如何正确的学习简介现在,越来越多的科技公司和开发者开始使用开发各种应用。 说明 2017-12-14 我发了一篇文章《没用过Node.js,就别瞎逼逼》是因为有人在知乎上黑Node.js。那篇文章的反响还是相当不错的,甚至连著名的hax贺老都很认同,下班时读那篇文章,竟然坐车的还坐过站了。大家可以很...
阅读 3714·2023-04-25 16:32
阅读 2090·2021-09-28 09:36
阅读 2010·2021-09-06 15:02
阅读 637·2021-09-02 15:21
阅读 894·2019-08-30 15:56
阅读 3485·2019-08-30 15:45
阅读 1669·2019-08-30 13:09
阅读 352·2019-08-29 16:05