面试题:假如有几十个请求如何控制并发(最优解)

在前端开发中,如果需要处理大量的并发请求,可以使用一些技术来控制并发数量,以避免过多的请求对服务器造成压力或导致浏览器 性能 问题。以下是几种常见的方法:

方法一:使用 Promise.all 和批量处理 (不受控并发)

方法:将请求分成多个批次,每个批次使用 Promise.all 进行并发处理。

const fetchData = async (urls) => {
  const batchSize = 5; // 每批次请求数量
  const results = [];  // 用于存储所有请求结果

  for (let i = 0; i < urls.length; i += batchSize) {
     // 每次取出 batchSize 个 URL,作为一个批次
    const batch = urls.slice(i, i + batchSize);

     // 并行执行当前批次的请求,并等待所有请求完成
    const batchResults = await Promise.all(
      batch.map(url => fetch(url).then(res => res.json()))
    );                  
     // 将当前批次的结果合并到 results 数组
    results.push(...batchResults);
  } 
  return results; // 返回所有批次请求的结果
};

// 示例
const urls = [
  'https://api.example.com/data1',
  'https://api.example.com/data2',
  // ... 其他 URL
];

fetchData(urls).then(results => {
  console.log(results);
});

优点:

  • 执行最快,所有请求同时进行。

缺点:

  • 可能导致请求过载,API 可能有并发限制(如 10 个/秒)。
  • 占用大量资源,影响页面性能。

方法二:使用 Promise.race() 控制并发  (最优解,推荐)

问题:有 8 个图片资源的 url,已经存储在数组 urls 中,而且已经有一个函数 function loadImg,输入一个 url 链接,返回一个 Promise,该 Promise 在图片下载完成的时候 resolve,下载失败则 reject。但是我们要求,任意时刻,同时下载的链接数量不可以超过 3 个。
请写一段代码实现这个需求,要求尽可能快速地将所有图片下载完成。

下图这样的排队和并发请求的场景基本类似,窗口只有三个,人超过三个之后,后面的人只能排队了。 

var urls = [
  "https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg",
  "https://www.kkkk1000.com/images/getImgData/gray.gif",
  "https://www.kkkk1000.com/images/getImgData/Particle.gif",
  "https://www.kkkk1000.com/images/getImgData/arithmetic.png",
  "https://www.kkkk1000.com/images/getImgData/arithmetic2.gif",
  "https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg",
  "https://www.kkkk1000.com/images/getImgData/arithmetic.gif",
  "https://www.kkkk1000.com/images/wxQrCode2.png",
];

function loadImg(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = function () {
      resolve(url); // 图片加载成功,返回 URL
    };
    img.onerror = reject;  // 加载失败时调用 reject
    img.src = url; // 触发图片加载
  });
}

async function limitLoad(urls, handler, limit) {
    // 存储所有加载任务
  const promises = []; 
  const queue = urls.splice(0, limit).map((url, index) => {
    const _p = handler(url); // 启动前 limit 个任务
    promises.push(_p);  // 记录该任务
    return _p.then((res) => {
     // 任务完成后返回任务索引和结果
      return [index, res];
    });
  });

  for (const item of urls) {
    const [index] = await Promise.race(queue);
    const _p = handler(item);
    promises.push(_p);
    queue[index] = _p.then((res) => {
      return [index, res];
    });
  }

  return Promise.allSettled(promises);
}

limitLoad(urls, loadImg, 3).then((res) => console.log(res));

 

优点:

  • 动态控制并发,确保始终有 limit 个任务在执行,不会阻塞队列
  • 更加高效,适用于大量请求的场景。

方法三:使用 p-limit 库(推荐)

p-limit 是一个用于限制并发数的库,可以更方便地控制并发请求数量。

安装 p-limit

npm install p-limit

使用 p-limit

import pLimit from 'p-limit';

const limit = pLimit(5); // 设置并发限制为 5

const fetchData = async (urls) => {
  const promises = urls.map(url => limit(() => fetch(url).then(res => res.json())));
  const results = await Promise.all(promises);
  return results;
};

// 示例
const urls = [
  'https://api.example.com/data1',
  'https://api.example.com/data2',
  // ... 其他 URL
];

fetchData(urls).then(results => {
  console.log(results);
});

优点:

p-limit 自动控制并发,代码更简洁。
可扩展性强,适合大规模并发请求

如何选择最优方案?

  1. 如果请求量小(<10),可以使用 Promise.all(),但不推荐大量请求时使用。
  2. 如果 API 有并发限制,建议使用 批量执行(方案 1)。
  3. 如果请求量大(几十到上百),最优解是Promise.race() 任务队列 或 p-limit 动态控制并发。

在 Vue3 + JS 中控制并发请求的最佳方式是使用 p-limit手写 asyncPool()。相比 Promise.all(),它能有效避免服务器压力过大,提高请求效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值