基于Promise实现一个限制并发请求的函数
1. 首先模拟一下请求方法
let getRequestFn = function(time){
return ()=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(time)
}, time);
})
}
} // 执行函数可返回一个自定义请求事件的函数,用来模拟请求
2. 实现一个限制并发数量的方法
首先我们可以思考一下如何可以限制并发,无非就是用一个循环
来判断当前的执行个数
,如果小于限制个数就再次发送请求
,很多小伙伴可能会写出这样的代码,比如:
function request(tasks,pool){
pool = pool || 5;
let results = [];
let running = 0;
while(tasks.length > 0){ // 如果还有请求未执行
if(running < pool) { // 如果小于并发次数 就再取出请求方法执行
let task = tasks.shift();
running++;
task().then(result => {
results.push(result); // 将执行结果存入数组
running --; // 当前执行个数减一
});
}
}
}
我们很容易忽略一个问题,js是单线程的
,函数执行过程中如果遇到异步任务,那么这个异步任务会退出主线程
,存放到任务队列
中等待执行,当主线程任务为空时才会执行异步任务,所以我们可以看到,代码中的running --
; 这个操做是无法被操作到的。因为while
一直在运行,导致异步任务无法获得主线程的执行。
使用递归实现限制并发数量的方法
// 要保证同时同时有pool个请求执行
function createRequest(tasks=[],pool){
pool = pool || 5; //限制并发的数量
let results = [];
let running = 0; // 当前运行个数
let resultsLength = tasks.length; // 用来判断最后的是否全部成功
return new Promise((resolve,reject)=>{
next();
function next(){
while(running < pool && tasks.length){ // 这个wile循环保证 一直有pool个请求在进行
running++;
let task = tasks.shift();
task().then(result => {
results.push(result);
}).finally(()=>{
running--;
next();
})
}
if(results.length === resultsLength) { // 全部执行结束
resolve(results);
}
}
})
}
我们使用一个next
函数来保证每次可以有怕pool
个请求函数在执行,并且超过两个时就可以退出循环
,这样可以执行到异步任务
,使得running--
;并且再次执行next()
用来执行请求的函数,最后当全部请求完毕以后返回results
,当然本函数没有处理请求失败的问题,大家有兴趣以可以添加。
- 测试
//创建模拟请求任务
let tasks = [getRequestFn(4000),getRequestFn(2000),getRequestFn(2000),getRequestFn(2000)];
// 发送请求 并发数为2
console.time();
createRequest(tasks,2).then((value)=>{
console.log(value)
console.timeEnd();
})
// 输出
[ 2000, 4000, 2000, 2000 ]
default: 6.016s
// 可以看到执行了6s 因为同时只有两个请求可以发送 2000, 4000 =》 2000 结束以后 又执行一个2000的 最后 4000结束 执行2000的 一共6s