实现思路
- 生成器函数:创建一个生成器函数,用于生成所有的异步请求任务。每次生成一个任务,通过
yield
暂停生成器,等待任务完成后再继续生成下一个任务。
- 任务队列:使用一个数组来存储所有待执行的任务,这些任务由生成器生成。
- 并发控制:利用一个计数器来记录当前处于活跃状态的请求数量,确保同一时间最多只有 5 个请求在执行。
- 重试机制:为每个请求设置最大重试次数,当请求失败时,在重试次数未达到上限的情况下,重新发起请求。
代码实现
function asyncRequest(url, maxRetries = 3) {
return new Promise((resolve, reject) => {
let retries = 0;
function sendRequest() {
// 模拟异步网络请求
setTimeout(() => {
const success = Math.random() > 0.5; // 模拟50%的失败率
if (success) {
resolve(`Successfully fetched from ${url}`);
} else if (retries < maxRetries) {
retries++;
sendRequest();
} else {
reject(new Error(`Failed to fetch from ${url} after ${maxRetries} retries`));
}
}, 1000);
}
sendRequest();
});
}
function* requestGenerator(urls) {
for (const url of urls) {
yield asyncRequest(url);
}
}
async function executeRequests(urls, maxConcurrent = 5) {
const tasks = [];
const gen = requestGenerator(urls);
let activeCount = 0;
function nextTask() {
const task = gen.next();
if (!task.done) {
tasks.push(task.value);
activeCount++;
task.value
.then(() => {
activeCount--;
if (tasks.length > 0) {
nextTask();
}
})
.catch(error => {
console.error(error);
activeCount--;
if (tasks.length > 0) {
nextTask();
}
});
}
}
while (activeCount < maxConcurrent && tasks.length < urls.length) {
nextTask();
}
await Promise.all(tasks);
}
// 示例使用
const urls = Array.from({ length: 100 }, (_, i) => `http://example.com/api/${i}`);
executeRequests(urls, 5);
生成器的关键作用
- 任务生成与暂停:生成器函数通过
yield
关键字可以暂停执行并返回一个值(即异步请求任务)。这使得我们可以按需逐个生成异步任务,而不是一次性生成所有任务,从而实现对任务的精细控制。
- 顺序执行与调度:生成器的
next()
方法可以恢复生成器的执行,通过这种方式,我们可以按照一定的顺序调度任务执行,结合并发控制逻辑,确保同一时间只有指定数量的任务处于活跃状态。
- 分离任务生成与执行:将任务的生成逻辑(在生成器函数中)与执行逻辑(在
executeRequests
函数中)分离,使得代码结构更加清晰,易于理解和维护。这种分离有助于在复杂的异步任务管理场景中,更好地组织和控制任务流。