优秀的编程知识分享平台

网站首页 > 技术文章 正文

【JS 并发请求】如何自定义并发请求数量

nanyue 2025-03-19 00:41:00 技术文章 5 ℃

前端并发通常涉及到同时发起多个网络请求、处理多个异步操作或同时执行多个JavaScript任务。今天和大家分享一下,如何设计一个可以控制并发数量的函数。

为了合理利用浏览器的请求并发数,我们一般会根据具体场景使用不同策略的并发请求:

  1. 自定义并发数量,单次请求成功之后,将后续请求依次放入请求队列里,保持固定的请求数量。如大文件切片上传,保证请求数量的同时,节省后续请求的时间。
  2. 限制并发数量,将请求分割成多个数组,并发数组内请求成功之后,在推入下一个请求数组,保证每次固定的请求数量。如模块内同时需要发送多个请求时导致的请求拥塞。

自定义高并发请求

示例图

并发数量5个



请求成功之后在推入后续的请求,始终保持5个并发数

代码如下

/**
 * 并发请求函数
 * @param urls - 请求的URL数组
 * @param count - 并发请求的数量
 * @returns 一个Promise,当所有请求完成时解析为结果数组
 */
export function concurrencyRequests(urls: string[], count: number) {
  // 如果URL数组为空,直接返回空数组Promise
  if(urls.length === 0) return Promise.resolve([]);
  
  // 返回一个新的Promise,用于处理并发请求
  return new Promise(async (resolve, reject) => {
    const results: any[] = []; // 存储请求结果的数组
    let currentCount = 0; // 当前并发请求数

    /**
     * 递归函数,用于处理下一个请求
     */
    const next = async() => {
      // 如果当前并发请求数大于等于URL数组长度,直接返回
      if(currentCount >= urls.length) return;
      const i = currentCount; // 记录当前请求的索引
      const url = urls[currentCount++]; // 获取当前请求的URL,并增加当前并发请求数

      const data = await fetch(url).then(res => res.json());
      results[i] = data; // 将请求结果存储到结果数组中

      // 如果结果数组长度等于URL数组长度,说明所有请求已完成,解析Promise
      if(results.length === urls.length) {
        resolve(results);
      }
      // 递归调用next函数,处理下一个请求
      next();
    }

    // 并发请求
    // 循环发起初始的并发请求,循环次数为Math.min(count, urls.length)
    for(let i = 0; i < Math.min(count,urls.length); i++) {
      next();
    }
  })
}

前端引入

const handleRequest = async () => {
   //具体的业务场景可以在 urls里自行配置
    const urls = Array.from({length: 10}, (_, i) => `/api/concurrency?q=${i}`)
    const res = await concurrencyRequests(urls,5)
    console.log('result',res)
}

结果返回

限制并发数量

示例图

并发数为5

前一组请求成功之后,再推入下一组请求


代码

/**
 * 处理API请求限制的函数
 * @param urls - 请求的URL数组
 * @param limit - 并发请求的限制数量,默认为5
 * @returns 一个Promise,当所有请求完成时解析为结果数组
 */
export async function handleApiLimit(urls: string[], limit = 5) {
  let result = [];
  let start = 0;
  // 初始化结束索引
  let end = limit;
  let length = urls.length;

  // 如果限制数量大于URL数组的长度,则将结束索引设置为URL数组的长度
  if (limit > length) {
    end = length;
  }

  // 循环处理URL数组,每次处理limit个URL
  while (start < length - 1 urllimiturlurlpromise let apis='[...urls.slice(start,'> fetch(url).then(res => res.json()));
    // 等待所有Promise完成,并将结果存储在res数组中
    const res = await Promise.all(apis);

    // 更新起始索引和结束索引
    start = end;
    end += limit;
    result.push(...res);
  }

  // 返回结果数组
  return result;
}

前端引入

const handleLimitRequest = async () => {
    const urls = Array.from({length: 10}, (_, i) => `/api/concurrency?q=${i}`)
    const res = await handleApiLimit(urls,5)
    console.log('result',res)
}

结果返回


最近发表
标签列表