🤔同时发送100个请求?!手撕,并发请求⌨️

💡问题出现 -> 要同时发100个请求

我们经常需要处理大量API请求。然而,

浏览器对同一域名下的并发请求数量有限制(通常是6-8个),
直接发送大量请求可能会导致性能问题甚至服务器过载

当我们需要发送几十甚至上百个请求时,如果一次性全部发出:

  • 浏览器限制 实际并发数量,导致 请求排队

  • 低配置服务器可能因突然的 高负载宕机

  • 带宽有限的服务器可能响应变慢

用这种方式模拟发100个请求

js 复制代码
const ids = new Array(100).fill('')
console.time()
for (let i = 0; i < ids.length; i++) {
  console.log(i) // 模拟发送请求
}
console.timeEnd()

💡 确定思想 -> 控制一批一批来

维护一个请求池(队列)

控制同时进行的请求数量(并发数)

当一个请求完成时,自动从队列中取出下一个请求

何为队列

队列是一种先进先出(FIFO)的数据结构,类似于现实生活中的排队。
新请求被添加到队列末尾(入队),而处理请求队列出(出队)。

代码

js 复制代码
export const handQueue = (reqs) => {
  reqs = reqs || []

  const requestQueue = (concurrency) => {
    concurrency = concurrency || 6 // 默认最大并发数
    const queue = [] // 请求池
    let current = 0 // 当前并发数

    // 出队函数,处理实际请求
    const dequeue = () => {
      while (current < concurrency && queue.length) {
        current++
        const requestPromiseFactory = queue.shift() // 从队列头部取出请求
        
        requestPromiseFactory()
          .then(() => {
            // 请求成功处理逻辑
          })
          .catch(error => {
            console.log(error) // 错误处理
          })
          .finally(() => {
            current-- // 释放一个并发位置
            dequeue() // 尝试处理下一个请求
          })
      }
    }

    // 返回入队函数
    return (requestPromiseFactory) => {
      queue.push(requestPromiseFactory) // 将请求加入队列
      dequeue() // 尝试处理请求
    }
  }

  // 创建并发数为6的队列
  const enqueue = requestQueue(6)

  // 将所有请求加入队列
  for (let i = 0; i < reqs.length; i++) {
    enqueue(() => fetch.get('/api/test' + i))
  }
}

代码解析

  1. handQueue函数:主函数,接收请求数组

  2. requestQueue函数 :创建请求队列,设置最大并发数
    concurrency:最大并发数,默认为6
    queue:存储待处理请求的队列
    current:当前正在处理的请求数

  3. dequeue函数 :核心处理逻辑 当当前并发数小于最大值队列不为空时,处理请求

    从队列中取出请求并执行

    无论请求成功或失败,最终都会减少当前并发数并尝试处理下一个请求

  4. 入队函数:将新请求加入队列并尝试处理

只有有请求响应成功的同时才会有新的请求进来,极大的降低了服务器的压力。

💡 用库也行

生产环境推荐库

p-limit

javascript 复制代码
import pLimit from 'p-limit';

const limit = pLimit(10);

const results = await Promise.all(
  requestList.map(request => 
    limit(() => request())
  )
);

async-pool

javascript 复制代码
import asyncPool from 'tiny-async-pool';

const results = await asyncPool(10, requestList, requestFn);

💡 讨论

思考🤔

问:为什么不用Promise.all

错误处理缺陷:一错全错

  • 问题Promise.all 会在一组 Promise 中任意一个失败时立即拒绝,导致整组请求被丢弃。
  • 后果:即使其他 9 个请求成功,只要第 10 个失败,整组结果都无法获取。
javascript 复制代码
Promise.all([request1, request2, request3])
  .then((results) => console.log(results)) // 全部成功才触发
  .catch((error) => console.error(error)); // 任意一个失败直接进入这里

思考🤔

那用Promise.allSettled行不行,可不可取?

它是可以失败的和成功的都等,但是,如果当中一个请求特慢,就会堵住后面的接口并发。

相关推荐
子兮曰1 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰1 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万1 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝1 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋1 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁1 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95271 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大1 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师1 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学1 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端