要求:多个请求做并发限制,请求完成后执行回调
思路:
首次循环启动能够执行的任务
取出能执行的任务推入执行器执行
执行器更新当前并发数,并且再请求完成时继续取出任务推入执行器
当所有请求完成时,触发回调函数
function sendRequest(requestList, limits, callback) {
const promises = requestList.slice(); // 取得请求list(浅拷贝一份)
// 得到开始时,能执行的并发数
const concurrentNum = Math.min(limits, requestList.length);
let concurrentCount = 0; // 当前并发数
// 第一次先跑起可以并发的任务
const runTaskNeeded = () => {
let i = 0;
// 启动当前能执行的任务
while (i < concurrentNum) {
i++;
runTask();
}
};
// 取出任务并且执行任务
const runTask = () => {
const task = promises.shift();
task && runner(task);
};
// 执行器
// 执行任务,同时更新当前并发数
const runner = async (task) => {
try {
concurrentCount++;
const res = await task();
console.log(res);
} catch (error) {
console.log(error);
} finally {
// 并发数--
concurrentCount--;
// 添加下一个任务
picker();
}
};
// 添加下一个任务
const picker = () => {
// 任务队列里还有任务并且此时还有剩余并发数的时候 执行
if (concurrentCount < limits && promises.length > 0) {
// 继续执行任务
runTask();
// 队列为空的时候,并且请求池清空了,就可以执行最后的回调函数了
} else if (promises.length == 0 && concurrentCount == 0) {
// 执行结束
callback && callback();
}
};
// 入口执行
runTaskNeeded();
}
测试一下上面的代码
function axios(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.6) {
resolve("成功");
} else {
reject("失败");
}
}, Math.random() * 2000);
});
}
const maxReqNum = 3,
requestList = [];
for (let k = 0; k < 15; k++) {
requestList.push(axios);
}
sendRequest(requestList, maxReqNum, function () {
console.log("所有请求完成");
});
要求:多个请求做并发限制,并且所有请求完成时,按照请求顺序返回结果
思路:
并发限制发送请求,需要发送的请求数量如果不够就一次完成了所有请求
每次完成一个请求就产生了一个空位,可以继续发送新的请求,通过索引来保证顺序
所有请求完成后(加了一个变量统计请求完成请求数量,并在finally中修改),按照请求发送的顺序返回所有请求的结果
function requestLimit(urls, limit) {
return new Promise((resolve) => {
let index = 0; // 每次请求的索引
// 存放所有请求的返回结果
const resultList = [];
if (urls.length === 0) return;
const limit = Math.min(maxReqNum, urls.length); // 第一次发送多少个请求
for (let i = 0; i < limit; i++) {
request();
}
let finishedCount = 0; // 请求完成的数量
// 发送请求
function request() {
const i = index; // i用来保证请求的顺序与相应顺序能对应上
const url = urls[index];
console.log(url);
index++; // 每次发送一次请求加1,保证请求一个接一个
axios(url)
.then((res) => {
resultList[i] = res + i;
})
.catch((err) => {
resultList[i] = err + i;
})
.finally(() => {
finishedCount++;
if (finishedCount === urls.length) {
resolve(resultList); // 所有请求完成,修改promise状态
}
if (index < urls.length) {
request(); // 没发送完一个请求,只要还有请求没有完成就继续发请求
}
});
}
});
}
// 模仿接口请求
function axios(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.6) {
resolve("成功");
} else {
reject("失败");
}
}, Math.random() * 5000);
});
}
// 测试一下,maxReqNum:最大并发数,urls:所有需要发送的请求
const maxReqNum = 3,
urls = [];
for (let k = 0; k < 15; k++) {
urls.push(`https:www.test.com/${k}`);
}
requestLimit(urls, maxReqNum)
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
要求:多个请求做并发限制;当请求失败时,继续发送这个请求,直到请求成功或者达到了同一个请求重复请求次数的限制(请求算完成);所有请求完成时,按照请求顺序返回结果
思路:
在2的基础上;添加了一个failUrl来判断是不是继续重复请求失败的那个;如果重复的请求,就不会往正在请求的任务中添加新的任务,保证并发的限制;请求失败的时候,需要判断请求失败的次数,通过往请求的信息中添加了一个index(保证请求失败时拿到正确的对应重复请求信息)和count保证重复的请求达到限制就不再发送
// 发送请求的方法
function request(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.6) {
resolve("成功");
} else {
reject("失败");
}
}, Math.random() * 2000);
});
}
function requestLimitFunc(maxReqNum, urlList, sameLimit) {
return new Promise((resolve) => {
let index = 0; // 通过索引,从urlList取出请求
const resultList = []; // 保留请求结果的数组
// 最多可以发多少个请求
const limit = Math.min(maxReqNum, urlList.length);
let finishCount = 0; // 有多少个请求已完成
function activeTask(params) {
for (let i = 0; i < limit; i++) {
excuteTask();
}
}
function excuteTask(failUrl) {
let reqUrl = "";
let i = 0;
if (!failUrl) {
i = index; // 如果传了failUrl,重新请求失败的那个,不需要往任务中添加请求了
reqUrl = urlList[index].url;
index++;
} else {
i = failUrl.reqIndex;
reqUrl = failUrl.url;
}
request(reqUrl)
.then((res) => {
finishCount++;
resultList[i] = res;
console.log(urlList[i].url, res);
// 一个请求完成,需要添加新的请求进来
if (index < urlList.length) excuteTask();
})
.catch((err) => {
// 请求失败需要重新发送请求,直到请求成功或达到最多发送次数
if (sameLimit === urlList[i].count) {
finishCount++;
resultList[i] = err;
console.log("失败了", urlList[i].url);
if (index < urlList.length) excuteTask();
} else {
urlList[i].count++;
excuteTask(urlList[i]);
}
})
.finally(() => {
if (finishCount === urlList.length) resolve(resultList);
});
}
activeTask(); // 第一次启动,在并发限制内发送最多的请求
});
}
// 等待请求的数组
const urlList = [];
for (let i = 0; i < 15; i++) {
urlList.push({
reqIndex: i,
count: 1,
url: `https:www.test.com/${i}`,
});
}
const maxReqNum = 5; // 最大并发数
const sameLimit = 3; // 同一个请求失败重新请求,最多请求3次
requestLimitFunc(maxReqNum, urlList, sameLimit).then((res) => {
console.log(res);
});