手写题

43 阅读1分钟

1. 并发下载器

实现一个 带请求池限制的并发下载器(下载多个请求,每次最多并发 n 个) ,比如最多同时发出 5 个请求,完成一个就发下一个,总共发 20 个。

✅ 目标理解

  • download(url, params):异步下载函数,返回一个 Promise
  • request({ pool: 5 }):限制最大并发数为 5 的请求调度器
  • for (let i = 0; i < 20; i++):模拟 20 个请求任务
  • 实现类似 请求池 的功能(并发控制)

工作机制

  • requestAjax 是一个包装函数,接受一个返回 Promise 的函数(即下载任务)。
  • 同时最多运行 pool 个任务(这里是 5)
  • 多余的任务会排队,等前面任务完成后自动执行
// 模拟一个下载函数,返回一个 Promise,模拟异步请求
const download = (url, params) => {
  return new Promise((resolve, reject) => {
    // 模拟网络请求延迟
    setTimeout(() => {
      resolve(`下载成功: ${url}`);
    }, 1000);
  });
};

// 实现请求池控制函数,限制并发数量
function request({ pool = 5 }) {
  let activeCount = 0;      // 当前正在执行的任务数量
  const queue = [];         // 等待中的任务队列

  // 尝试从队列中取出任务执行
  const runNext = () => {
    if (queue.length === 0 || activeCount >= pool) return; // 无任务或达到并发上限就不执行
    const { fn, resolve, reject } = queue.shift(); // 从队列中取出一个任务
    activeCount++; // 当前活跃任务数 +1
    fn()           // 执行任务函数(必须返回 Promise)
      .then(resolve)   // 成功后调用传入的 resolve
      .catch(reject)   // 失败后调用传入的 reject
      .finally(() => {
        activeCount--; // 当前任务完成,活跃数 -1
        runNext();     // 执行下一个任务(如果有)
      });
  };

  // 返回一个包装函数,接收一个返回 Promise 的函数作为任务
  return function (fn) {
    return new Promise((resolve, reject) => {
      queue.push({ fn, resolve, reject }); // 将任务推入队列
      runNext(); // 尝试执行任务
    });
  };
}

// 创建一个最大并发数为 5 的请求控制器
const requestAjax = request({ pool: 5 });

// 向任务池中添加 20 个下载任务
for (let i = 0; i < 20; i++) {
  requestAjax(() => download(`https://example.com/file_${i}.jpg`));
}