并发控制的概念相信大家都很熟悉了,比如浏览器请求的并发控制。今天,我们结合开源工具async-pool,看看如何实现一个简单的并发控制。async-pool的代码分为es6和es7两个版本,都非常简单。我们主要基于es6版本进行讲解。去掉参数验证等逻辑后,核心代码如下,非常简短:functionasyncPool(poolLimit,array,iteratorFn){leti=0;constret=[];const执行=[];constenqueue=function(){if(i===array.length){returnPromise.解决();}constitem=array[i++];constp=承诺。解决()。然后(()=>iteratorFn(item,array));ret.push(p);让r=Promise.resolve();if(poolLimit<=array.length){conste=p.then(()=>executing.splice(executing.indexOf(e),1));执行。推(e);if(executing.length>=poolLimit){r=Promise.比赛(执行);}}返回r。然后(()=>排队());};returnenqueue().then(()=>Promise.all(ret));}asyncPool支持三个参数,第一个是并发请求数,第二个是一组请求输入,第三个是迭代函数返回承诺。我们举个例子来说明。假设我们现在有500个请求要发送,并发数控制为50。那么我们就可以这样使用asyncPool了:asyncPool(50,[/*500个请求的参数数据*/],()=>{/*发起请求的函数*/})现在我们来详细解释一下asyncPool是如何工作的。首先在asyncPool中初始化两个数组,ret保存返回结果,其顺序必须与输入顺序一致,executing用于记录当前正在执行的请求。在asyncPool中创建了一个enqueue函数,负责具体的并发控制逻辑。在enqueue函数中,通过变量i逐一获取请求入参,调用迭代函数发起请求,然后将返回的promise保存在ret中。constitem=array[i++];constp=Promise.resolve().then(()=>iteratorFn(item,array));ret.push(p);之后就是并发量控制的核心逻辑:),1));执行.push(e);if(executing.length>=poolLimit){r=Promise.race(executing);}}returnr.then(()=>enqueue());如果并发数限制大于要发起的请求数,则不需要通过executing数组来记录正在执行的请求,直接循环发起请求即可。如果并发数限制小于要发起的请求数,则先将之前调用迭代函数返回的promise生成一个新的promise,并投入执行。当这个新的承诺履行时,将其从执行中删除。如果执行数组的长度大于并发数控制,则使用Promise.race(executing)获取第一个返回的promsie,用于下一次迭代。通过变量r我们可以看出,在整个循环过程中,enqueue函数会形成一个promise链。最后一个promise返回后,asyncPool会通过Promise.all返回所有的结果。returnenqueue().then(()=>Promise.all(ret));至此,我们就分析完了async-pool的核心逻辑。以上分析过程是基于es6版本的代码,es7版本更加简洁,如下,读者可以自行分析:asyncfunctionasyncPool(poolLimit,array,iteratorFn){constret=[];const执行=[];for(constitemofarray){constp=Promise.resolve().then(()=>iteratorFn(item,array));ret.push(p);if(poolLimit<=array.length){conste=p.then(()=>executing.splice(executing.indexOf(e),1));执行.push(e);if(executing.length>=poolLimit){awaitPromise.race(executing);}}}returnPromise.all(ret);}我们知道,不管是Promise.race还是Promise.all,只要一个promise达到Fufilled或者Rejected状态,就会全部返回。这在接口请求的上下文中是不合适的。我们应该如何改造它?其实也很简单,只要在迭代函数的调用处做一些特殊的处理就可以了。iteratorFn(item,array).then(resp=>resp).catch(error=>error);常见面试知识点、技术方案、教程可通过扫描二维码关注公众号“千里千寻”获取,或来这里https://everfind.github.io。
