阅读视图

发现新文章,点击刷新页面。

js并发请求,且限制并发请求数量实现方案

说明

  • 前端会遇到这种需求,希望多个请求并发加快前端反应速度,但又要限制同时请求的数量,防止服务器压力过大
  • 以下代码由Trae里的千问模型生成,思路真牛

方案

async function batchQuset(){
      this.$store.commit('openLoading')
      const questNum = this.paramsList.length
      // 每批最多 3 个并发请求
      const BATCH_SIZE = 3
      // 保持顺序的结果数组
      const numResList = new Array(questNum).fill(null) 
      // 并发控制函数
      const runWithConcurrency = async (tasks, concurrency) => {
        const results = []
        // 维护一个正在执行的 Promise 列表(控制并发就靠它)
        const executing = []
        for (const task of tasks) {
          // p 是 then() 方法返回的新 Promise,不是 task() 的原始 Promise
          const p = task().then((result) => {
            // 当task执行完成后,从executing数组中移除
            executing.splice(executing.indexOf(p), 1)
            return result
          })
          results.push(p)
          executing.push(p)
          // 当正在执行的任务数量超过并发限制时,等待任意一个完成
          if (executing.length >= concurrency) {
            // 等有完成的Promise时,外面的for循环才能继续走
            await Promise.race(executing)
          }
        }
        // 走到这里时,仅剩下小于并发数量的Promise还没彻底完成
        return Promise.all(results)
      }
      // 创建所有校验任务[Promise,Promise]
      const tasks = this.paramsList.map(
        (params, index) => async () => {
          const { pass, res } = await questApi(params)
          this.$store.commit(
            'openLoading',
            `进度: ${index + 1}/${questNum}`
          )
          return { index, pass, res }
        }
      )
      // 执行并发校验
      const results = await runWithConcurrency(tasks, BATCH_SIZE)
      // 按原始顺序填充结果
      results.forEach(({ index, pass, res }) => {
        if (!pass) {
          numResList[index] = res
        }
      })
      // 过滤掉 null 值,得到最终的列表
      const resList = numResList.filter((item) => item !== null)
      this.$store.commit('closeLoading')
      // *****请求结果resList进行展示/处理******
}

说明

  • this.$store.commit('openLoading')是自己把element的loading二次封装,方便唤起和改文字
import Vuex from "vuex";
import { Loading } from 'element-ui';
export default new Vuex.Store({
    state: {
    // 全屏loading实例
    loading: null,
    },
    mutations:{
     openLoading(state, str) {
      if (state.loading) {
        state.loading.setText(str)
      } else {
        state.loading = Loading.service({
          lock: true,
          text: str,
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)',
          customClass: 'phone-loading'
        })
      }
    },
    closeLoading(state) {
      state.loading?.close()
      state.loading = null
    },
    }
})
  • then 中 return result 的作用 :
    • 当 then 的回调函数执行完毕后, p 这个 Promise 会被 resolve
    • p 被 resolve 的值就是 return 后面的值
    • 如果 return result ,则 p 解析为 result
    • 如果不 return 或 return undefined ,则 p 解析为 undefined
// 示例 1:return result
const p1 = task().then((result) => {
  console.log('收到 result:', result)  // 假设 result = '任务完成'
  return result  // return '任务完成'
})

p1.then((value) => {
  console.log('p1 的值:', value)  // 输出:'任务完成'
})

// 示例 2:不 return
const p2 = task().then((result) => {
  console.log('收到 result:', result)  // 假设 result = '任务完成'
  // 没有 return 语句
})

p2.then((value) => {
  console.log('p2 的值:', value)  // 输出:undefined
})

// 示例 3:return 其他值
const p3 = task().then((result) => {
  console.log('收到 result:', result)  // 假设 result = '任务完成'
  return '修改后的值'
})

p3.then((value) => {
  console.log('p3 的值:', value)  // 输出:'修改后的值'
})
❌