当前位置: 首页 > 后端技术 > Node.js

15行代码实现并发控制(javascript)

时间:2023-04-03 14:07:48 Node.js

前言首发于github博客做过爬虫的都知道,控制爬虫的并发请求,其实就是控制爬虫的频率,避免被封通过知识产权。这用于控制爬虫应用程序的运行内存。不然一次处理N个请求,分分钟内存就爆了。Python爬虫一般使用多线程来控制并发,但是如果是node.js爬虫,由于其单线程非阻塞的特性和事件循环机制,一般不需要多线程来控制并发(当然node.js也可以实现多线程,这里不讲重点),但是直接在代码层面实现并发更简单。为了方便,开发者在开发节点爬虫时,通常会找一个并发控制的npm包。但是,有时候第三方模块并不能完全满足我们的特殊需求。这个时候,我们可能需要定制版的并发控制。功能。下面我们用15行代码来实现一个并发控制功能。具体实现参数首先,一个基本的并发控制函数基本上有以下三个参数:list{Array}-要迭代的数组limit{number}-并发控制的个数asyncHandle{function}-对列表中每一项的处理函数设计下面以爬虫为例进行说明,设计思路其实很简单。如果先并发控制为5,则立即发送5个异步请求,我们得到5个并发异步请求//limit=5while(limit--){handleFunction(list)}那么,不管这5个异步请求是哪个先执行,会继续执行下一个列表项letrecursion=(arr)=>{returnasyncHandle(arr.shift()).then(()=>{//迭代数组长度不为0,递归执行自身if(arr.length!==0)returnrecursion(arr)//迭代数组长度为0,endelsereturn'finish';})}列表中所有项迭代完成后回调returnPromise.all(allHandle)以上步骤的组合就是/***@paramslist{Array}-要迭代的数组*@paramslimit{Number}-并发数控制Number*@paramsasyncHandle{Function}-`list`每一项的handler函数,参数为当前处理的项,必须返回一个Promise来判断是否继续迭代*@return{Promise}-返回一个Promise值来确认所有数据迭代是否完成*/letmapLimit=(list,limit,asyncHandle)=>{letrecursion=(arr)=>{returnasyncHandle(arr.shift()).then(()=>{if(arr.length!==0)returnrecursion(arr)//数组还没迭代完,递归继续迭代elsereturn'finish';})};让listCopy=[].concat(列表);让asyncList=[];//正在进行的所有并发异步操作while(limit--){asyncList.push(recursion(listCopy));}返回Promise.all(asyncList);//所有并发异步操作完成后,本次并发控制迭代完成}模拟异步并发的测试demovardataLists=[1,2,3,4,5,6,7,8,9,11,100,123];varcount=0;mapLimit(dataLists,3,(curItem)=>{returnnewPromise(resolve=>{count++setTimeout(()=>{console.log(curItem,'currentconcurrency:',count--)resolve();},Math.random()*5000)});}).then(response=>{console.log('finish',response)})结果如下:手动抛出异常中断并发函数测试:vardataLists=[1,2,3,4,5,6,7,8,9,11,100,123];varcount=0;mapLimit(dataLists,3,(curItem)=>{returnnewPromise((resolve,reject)=>{count++setTimeout(()=>{console.log(curItem,'当前并发数:',count--)if(curItem>4)reject('错误发生')resolve();},Math.random()*5000)});}).then(response=>{console.log('finish',response)})在并发控制的情况下,迭代到5、6、7并手动抛出异常以停止后续迭代: