侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现一个支持并发限制的异步请求队列

2025-12-12 / 0 评论 / 4 阅读

题目

实现一个支持并发限制的异步请求队列

信息

  • 类型:问答
  • 难度:⭐⭐

考点

Promise, async/await, 并发控制, ES6+异步编程

快速回答

实现一个异步请求队列函数 asyncQueue(requests, limit),要求:

  • 接收请求数组和并发限制数
  • 使用 Promise 控制并发
  • 所有请求完成后返回结果数组
  • 保持结果顺序与输入一致

核心实现步骤:

  1. 使用 Array.map 创建待执行队列
  2. 通过递归函数控制并发数量
  3. 使用 Promise.race 动态管理执行池
  4. 利用 async/await 处理异步流程
## 解析

问题背景

在前端开发中,经常需要处理大量异步请求(如API调用)。浏览器对并发请求数有限制(通常6-8个),无限制并发会导致:

  • 网络资源竞争导致部分请求阻塞
  • 服务器压力过大可能拒绝服务
  • 客户端性能下降

解决方案

async function asyncQueue(requests, limit) {
  const results = [];
  const executing = [];

  for (const [i, request] of requests.entries()) {
    const p = Promise.resolve().then(() => request());
    results[i] = p;

    const e = p.then(() => executing.splice(executing.indexOf(e), 1));
    executing.push(e);

    if (executing.length >= limit) {
      await Promise.race(executing);
    }
  }

  return Promise.all(results);
}

原理说明

  • Promise.race 动态控制:通过不断检查执行池,确保运行中的请求不超过限制
  • 结果顺序保持:提前在结果数组对应位置存储Promise,与输入顺序一致
  • 执行池管理:每个请求完成后自动从执行池移除自身

使用示例

// 模拟异步请求
const mockRequest = (id, delay) => () => 
  new Promise(resolve => 
    setTimeout(() => resolve(`请求${id}完成`), delay)
  );

// 创建10个请求
const requests = [];
for (let i = 1; i <= 10; i++) {
  requests.push(mockRequest(i, Math.random() * 2000));
}

// 并发限制为3
asyncQueue(requests, 3)
  .then(results => console.log(results));
// 输出:["请求1完成", "请求2完成", ...](保持原始顺序)

最佳实践

  • 错误处理:在请求函数内添加 .catch 保证单个失败不影响整体
  • 性能优化:根据实际网络环境动态调整并发数
  • 取消机制:可结合 AbortController 实现请求中断

常见错误

错误类型示例解决方案
结果顺序错乱直接使用 Promise.all提前固定结果数组索引
并发控制失效使用 for...of 无等待正确使用 await Promise.race
内存泄漏未清理执行池引用请求完成后立即移除

扩展知识

  • Promise 组合器Promise.allSettled 可获取所有请求状态(包含失败)
  • 高级模式:令牌桶算法、漏桶算法等更精细的流量控制
  • 实际应用:图片懒加载队列、大文件分片上传、批量数据提交