假如给你一个数组,里面是请求路径,如何设计一个函数去控制这些请求的并发呢?
这里我们用的请求路径为https://jsonplaceholder.typicode.com/todos来模拟
const reqArr = []; for (let i = 1; i <= 10; i++) { reqArr.push(`https://jsonplaceholder.typicode.com/todos/${i}`); }
第一种方案:基于Promise实现:
首先定义一个函数,并且确定其形参,第一个为传入的请求路径数组,第二个为需要限制的并发数,并且返回值为Promise。
function limitReqFn(urlArray, limitNum){
return new Promise(resolve)=>{}
}
第一步:首先我们需要对传入的路径数组判断一下是否为空。
if (urlArray.length === 0) {//如果为空就直接resolve出去 resolve([]); return; }
第二步:我们需要定义一个result数组用于接收请求响应结果,并且定义当前请求路径的index值下标currentRequetIndex,以及一个异步请求函数request
const result = []; let currentRequetIndex = 0; async function request(){}
第三部:完善request逻辑,核心逻辑就是拿到当前请求的路径并且进行网络请求,然后将结果放到result里,需要注意的就是拿到请求结果res后不能直接用push方法放到result里,因为返回的顺序可能不一致。 需要拿一个临时变量i存储currentRequetIndex,然后再将结果数组result的下标i的值设为请求返回的结果res
async function request() { if (currentRequetIndex === urlArray.length) { //当索引下标等于传入的url数组时返回 return; } const i = currentRequetIndex;//临时存储currentRequestIndex const currentReqUrl = urlArray[currentRequetIndex];//拿到当前请求路径 currentRequetIndex++; //将currentRequestIndex进行+1,指向下一个请求路径 try { const res = await fetch(currentReqUrl); result[i] = res;//将结果赋值给result数组 } catch (error) { result[i] = error; } finally {//请求完后判断result的长度是否和传入的数组相同,然后返回结果 if (result.length === urlArray.length) { resolve(result); } request();//将下一个路径拿来请求 } }
第四部:设置并发数量,并且进行请求
const times = Math.min(limitNum, urlArray.length);//将limitNum和传入的数组长度做一下比较 for (let i = 0; i < times; i++) { request(); }
完整代码:
const reqArr = []; for (let i = 1; i <= 10; i++) { reqArr.push(`https://jsonplaceholder.typicode.com/todos/${i}`); } function limitReqFn(urlArray, limitNum) { return new Promise((resolve) => { if (urlArray.length === 0) { resolve([]); return; } const result = []; let currentRequetIndex = 0; async function request() { if (currentRequetIndex === urlArray.length) { return; } const i = currentRequetIndex; const currentReqUrl = urlArray[currentRequetIndex]; currentRequetIndex++; try { const res = await fetch(currentReqUrl); result[i] = res; } catch (error) { result[i] = error; } finally { if (result.length === urlArray.length) { resolve(result); } request(); } } const times = Math.min(limitNum, urlArray.length); for (let i = 0; i < times; i++) { request(); } }); } limitReqFn(reqArr,2).then((data)=>console.log(data));
接下来我们到浏览器里去试一试,f12打开network,切换到fast 3G模拟慢网
第二种方案:采用数组令牌的方式。
第一步:我们根据传入的并发条数创建一个对应的token数组,在请求的时候,需要从数组中拿一个token作为标识,然后请求完成后将token归还回去,然后定义一个等待队列,一个执行队列。
function limitRequest(rqArr, length) { const tokenKey = Array.from({ length }, (v, k) => `token${k}`);//创建token标识数组 const backToken = function (token) { //归还token的方式 tokenKey.push(token); }; const getTokenKey = function () { //获取一个token return tokenKey.splice(0, 1)[0]; }; let waitQueen = [];//等待队列 let exectuor = [];//执行队列 }
第二步:我们需要定义一个方法将对应的请求带上token存入执行队列,如果没拿到token的请求就放入等待队列。
const generateFn = function (arr, clear=false) { if (clear) {// 清空队列 exectuor = []; } for (let i = 0; i < arr.length; i++) { if (tokenKey && tokenKey.length) {//如果还有token剩余 const reqObj = { token: getTokenKey(), url: arr[i], }; exectuor.push(reqObj);//将请求添加到执行队列 } else { waitQueen.push(rqArr[i]);//没有拿到token就添加到等待队列 } }runExectuor();//执行队列生成完后,就到执行队列里去请求
};
第三步:我们已经生成了执行队列,这个时候就需要定义一个执行队列里请求的方法。
const runExectuor = function () { for (const fn of exectuor) { fetch(fn["url"]).then((v) => { backToken(fn.token);//执行完成一个请求后就归还token if (waitQueen && waitQueen.length) {//如果等待队列里有请求就拿出来放到执行队列里 generateFn(waitQueen.splice(0, 1), true);//每次生成需要清空队列 } }); } };
完整代码:
function limitRequest(rqArr, length) { const tokenKey = Array.from({ length }, (v, k) => `token${k}`); const result = []; const backToken = function (token) { tokenKey.push(token); }; const getTokenKey = function () { return tokenKey.splice(0, 1)[0]; }; let waitQueen = []; let exectuor = []; const generateFn = function (arr, type=false) { if (type) { exectuor = []; } for (let i = 0; i < arr.length; i++) { if (tokenKey && tokenKey.length) { const reqObj = { token: getTokenKey(), url: arr[i], }; exectuor.push(reqObj); } else { waitQueen.push(rqArr[i]); } } runExectuor(); }; const runExectuor = function () { for (const fn of exectuor) { fetch(fn["url"]).then((v) => { backToken(fn['token']); if (waitQueen && waitQueen.length) { generateFn(waitQueen.splice(0, 1), true); } }); } }; generateFn(rqArr); } const reqArr = []; for (let i = 1; i <= 10; i++) { reqArr.push(`https://jsonplaceholder.typicode.com/todos/${i}`); } limitRequest(reqArr,2);
好啦,今天就分享这2种解决并发控制方法和思路。
标签:function,const,请求,知识,token,并发,length,result From: https://www.cnblogs.com/micbin/p/17655006.html