Skip to content

前端并发限制方案

在前端开发中,限制并发请求 是一个常见的需求,特别是在 API 请求、文件上传、爬虫爬取数据 等场景。以下是几种通用的前端并发控制方法,可用于 React、Vue、Node.js、浏览器环境 等。


1️. Promise.all + 批量执行(简单分批)

如果你想 批量执行多个请求,但一次性不要超出 n 个并发,你可以手动分批执行

js
async function fetchWithLimit(urls, limit) {
  let results = [];
  let index = 0;

  async function fetchBatch() {
    const batch = urls.slice(index, index + limit).map(url=>
	    fetch(url).then(res => res.json()));
    index += limit;

    const batchResults = await Promise.all(batch);
    results = results.concat(batchResults);

    if (index < urls.length) {
      await fetchBatch();
    }
  }

  await fetchBatch();
  return results;
}

const urls = ["/api/data1", "/api/data2", "/api/data3", "/api/data4", "/api/data5"];
fetchWithLimit(urls, 2).then(data => console.log("所有数据:", data));
  • 小批量并发请求(如 10~50 个请求)
  • 不需要动态管理,只需要限制批量请求

2. Promise.race + 任务队列(动态控制并发)

如果你希望 限制并发,但可以动态管理任务队列(即:前一个任务完成后再加入新的请求),可以使用 Promise.race

js
async function fetchWithConcurrencyLimit(urls, limit) {
  let results = [];
  let executing = [];

  async function enqueue(url) {
    const promise = fetch(url).then(res => res.json());
    results.push(promise);

    // 任务完成后从列表中移除
    const e = promise.then(() => executing.splice(executing.indexOf(e), 1));
    executing.push(e);

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

  for (const url of urls) {
    await enqueue(url);
  }

  return Promise.all(results);
}

const urls = ["/api/data1", "/api/data2", "/api/data3", "/api/data4", "/api/data5"];
fetchWithConcurrencyLimit(urls, 2).then(data => console.log("所有数据:", data));

📌 适用场景

  • 大批量并发请求(如 100+)
  • 保证前一个任务完成后,自动加入新任务
  • 动态控制并发量

3. p-limit(NPM 依赖,更优雅的并发管理)

如果你的项目使用 Node.js / Webpack / React / Vue,可以使用 p-limit 这个库,它可以 自动管理并发任务

📌 安装 p-limit

bash
npm install p-limit

📌 使用 p-limit 限制并发

javascript
import pLimit from "p-limit";

const limit = pLimit(2); // 限制 2 个并发请求

const urls = ["/api/data1", "/api/data2", "/api/data3", "/api/data4", "/api/data5"];
const tasks = urls.map(url => limit(() => fetch(url).then(res => res.json())));

Promise.all(tasks).then(data => console.log("所有数据:", data));

📌 适用场景

  • Node.js / React / Vue 项目
  • 优雅的并发控制
  • 适用于文件上传、爬虫、任务队列

4. async/await + setTimeout(逐步发起请求)

如果你需要 逐步发起请求(而不是全部并发),可以使用 setTimeout 控制请求间隔。

📌 示例:每 1 秒发起 2 个请求

javascript
async function fetchInBatches(urls, batchSize, delay) {
  let results = [];

  for (let i = 0; i < urls.length; i += batchSize) {
    const batch = urls.slice(i, i + batchSize);
    const batchResults = await Promise.all(batch.map(url =>
	    fetch(url).then(res => res.json())));
    results = results.concat(batchResults);

    if (i + batchSize < urls.length) {
      await new Promise(resolve => setTimeout(resolve, delay)); // 控制请求节奏
    }
  }

  return results;
}

const urls = ["/api/data1", "/api/data2", "/api/data3", "/api/data4", "/api/data5"];
fetchInBatches(urls, 2, 1000).then(data => console.log("所有数据:", data));

📌 适用场景

  • 前端首屏加载优化
  • 逐步加载数据,避免一次性请求过多
  • 防止服务器高峰期压力过大

5. 使用 AbortController 取消并发请求

如果你希望可以 在某个时刻取消正在进行的请求(如:用户离开页面、组件卸载),可以结合 AbortController

📌 示例:取消正在进行的请求

js
let controller = new AbortController();

async function fetchData(url) {
  try {
    const response = await fetch(url, { signal: controller.signal });
    return await response.json();
  } catch (error) {
    if (error.name === "AbortError") {
      console.log("请求被取消:", url);
    } else {
      console.error("请求失败:", error);
    }
  }
}

const urls = ["/api/data1", "/api/data2", "/api/data3"];
const requests = urls.map(url => fetchData(url));

// 3 秒后取消所有请求
setTimeout(() => {
  controller.abort();
  console.log("所有请求已取消");
}, 3000);

📌 适用场景

  • 前端组件卸载时,避免请求继续执行
  • 用户离开页面时,取消未完成的 API 调用
  • 动态终止不必要的并发任务

6. 总结

方法适用场景关键点
批量请求(Promise.all小批量并发(10~50 个请求)手动分批
任务队列(Promise.race动态控制并发任务(100+ 请求)任务完成后加入新任务
p-limit(NPM 库)Node.js / React / Vue 项目自动管理并发
逐步发起(setTimeout逐步加载,避免服务器压力限制请求速率
AbortController取消正在进行的请求终止无用 API 调用

✅ 推荐使用

  • 简单请求Promise.all()
  • 大量请求Promise.race()
  • 更优雅的控制p-limit
  • 逐步请求,减少压力setTimeout
  • 可以取消请求AbortController

如果你要做 通用的前端并发限制p-limitPromise.race 方式会更灵活,适用于大多数情况!🚀

评论区

欢迎留言、补充或勘误。

xiaoba.blog