const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const makeRequest = async (id) => {
console.log(`Request ${id} started`);
await sleep(Math.random() * 2000); // 模拟网络请求
console.log(`Request ${id} completed`);
return id;
};
const concurrentRequests = async (requests, maxConcurrency) => {
let currentIndex = 0;
const results = [];
const pendingPromises = [];
const enqueue = () => {
if (currentIndex >= requests.length) {
return Promise.resolve();
}
const requestIndex = currentIndex++;
const request = requests[requestIndex];
const promise = request().then(result => {
results[requestIndex] = result;
});
pendingPromises.push(promise);
const removePromise = () => {
pendingPromises.splice(pendingPromises.indexOf(promise), 1);
};
promise.then(removePromise).catch(removePromise);
const nextPromise = promise.then(() => enqueue());
if (pendingPromises.length >= maxConcurrency) {
return Promise.race(pendingPromises).then(() => nextPromise);
} else {
return nextPromise;
}
};
await enqueue();
return Promise.all(pendingPromises).then(() => results);
};
const requests = Array.from({ length: 100 }, (_, i) => () => makeRequest(i + 1));
concurrentRequests(requests, 5).then(results => {
console.log('All requests completed');
console.log(results);
});
模拟请求:
makeRequest 是一个模拟网络请求的函数,接受一个 id 参数,模拟请求完成需要一段时间(通过 sleep 函数实现)。
控制并发请求:
concurrentRequests 函数负责控制并发请求的数量。它接受两个参数:requests 是一个函数数组,每个函数返回一个 Promise;maxConcurrency 是最大并发请求数。
请求调度:在这里插入代码片
使用 currentIndex 追踪当前请求的位置。
results 用于存储每个请求的结果。
pendingPromises 存储当前正在进行的请求。
enqueue 函数处理请求队列,根据 maxConcurrency 控制并发请求数。
执行并发请求:
使用 Promise.race 等待其中一个请求完成,以便在完成后继续处理新的请求。
启动请求:
生成包含100个请求的数组,每个请求都是一个返回 Promise 的函数。
调用 concurrentRequests 函数并传入请求数组和最大并发数(5)。
更简单的方法!
维护一个运行池一个等待队列,出一个进一个,控制运行池的大小
const pool = new Set();
const waitQueue = [];
max = 10 //最大并发数
const request = v => {
return new Promise((res, rej) => {
const isfull = pool.size > max;
const fn = () => {
const request = v();
request.finally(() => {
pool.delete(fn);
const next = waitQueue.shift();
next && pool.add(next);
next?.(); //相当于执行fn
request.then(res);
request.catch(rej);
return request;
})
}
if (isfull) {
waitQueue.push(fn);
} else {
pool.add(fn);
fn();
}
})
}
let arr = [];
//模拟一步请求
for (let i = 0; i < 100; i++) {
arr.push(() => new Promise((resolve) => {
setTimeout(() => {
console.log('done', i);
resolve();
}, 100 * i);
}));
};
arr.map((v, index) => { request(v).then(() => { console.log(index) }) })
解释:
isfull表示运行池是否满了,满了就把这个请求放在waitQueue中,没有满就放在运行池pool中直接执行,此时pool中有十个请求在同时进行, 十个异步请求但凡有一个执行完,就在从waitQueue拿出一个放在pool中继续执行,所以每个request都要定义finally后的回调