vue 请求并发设置最大并发量

const privateWeakMap = new WeakMap();
function getConcurrent(_this) {
    return privateWeakMap.get(_this)
}
class ConcurrentInterceptors {
    /**
     * 最大并发数量
     * @param maxProgressCount
     * @typeof number
     */
    constructor(maxProgressCount) {
        privateWeakMap.set(this,
            {
                // 最大并发数量
                _maxProgressCount: maxProgressCount,
                // 进行中的任务数量
                _inProgressCount: 0,
                // 等待中的任务
                _waitingArr: [],
            }
        );
    }

    /**
     * 拦截器
     * @return Promise.resolve()
     */
    interceptors = () => {
        const { _inProgressCount, _maxProgressCount } = getConcurrent(this)
        return new Promise(resolve => {
            // 非数字、isNaN、不大于0 不拦截
            if (typeof _maxProgressCount !== 'number' || isNaN(_maxProgressCount) || _maxProgressCount <= 0) {
                resolve()
                return
            }
            // 当前进行的任务数量是否大于最大并发数量
            if (_inProgressCount < _maxProgressCount) {
                // 未超出最大并发数量
                getConcurrent(this)._inProgressCount += 1
                resolve()
            } else {
                // 超出最大并发数量
                getConcurrent(this)._waitingArr.push(resolve);
            }
        });
    }

    /**
     * 启动下一个被拦截的任务
     */
    next = () => {
        // 当前进行中的数量减一
        getConcurrent(this)._inProgressCount -= 1

        // 等待发送的请求的移除一个
        const nextResolve = getConcurrent(this)._waitingArr.shift();
        if (nextResolve) {
            // 要进行下一个任务了 当前进行中的数量加一
            getConcurrent(this)._inProgressCount += 1
            nextResolve();
        }
    }

    /**
     * 更新最大并发数量
     * @param newMaxProgressCount
     */
    updateMaxProgressCount = (newMaxProgressCount) => {
        getConcurrent(this)._maxProgressCount = newMaxProgressCount;
    }

    /**
     * 获取_maxProgressCount属性值
     */
    get maxProgressCount() {
        return getConcurrent(this)._maxProgressCount
    }
    set maxProgressCount(newVal) {
        this.updateMaxProgressCount(newVal)
    }

    /**
     * 获取_sendAxiosIngCount属性值
     */
    get inProgressCount() {
        return getConcurrent(this)._inProgressCount
    }
    set inProgressCount(newVal) {
        throw new Error(`inProgressCount为只读属性`);
    }

    /**
     * 获取_awaitSendAxiosArr属性值
     */
    get waitingArr() {
        return getConcurrent(this)._waitingArr
    }
    set waitingArr(newVal) {
        throw new Error(`waitingArr为只读属性`);
    }
}

export default ConcurrentInterceptors
AI代码解释:
这段代码定义了一个 ConcurrentInterceptors 类,用于管理并发任务的拦截。其核心目的是控制最大并发任务数,并处理超出最大并发数的任务。下面是对代码的详细解释:

1. privateWeakMap 和 getConcurrent 函数
const privateWeakMap = new WeakMap();
function getConcurrent(_this) {
    return privateWeakMap.get(_this)
}
privateWeakMap 是一个 WeakMap 对象,用于存储 ConcurrentInterceptors 实例的私有数据。
getConcurrent 函数用于从 privateWeakMap 中获取指定实例 _this 的私有数据。
2. ConcurrentInterceptors 类
构造函数
constructor(maxProgressCount) {
    privateWeakMap.set(this,
        {
            _maxProgressCount: maxProgressCount,
            _inProgressCount: 0,
            _waitingArr: [],
        }
    );
}
构造函数接收一个参数 maxProgressCount,表示最大并发任务数。
将实例的私有数据(最大并发数、进行中的任务数和等待中的任务数组)存储到 privateWeakMap 中。
interceptors 方法
interceptors = () => {
    const { _inProgressCount, _maxProgressCount } = getConcurrent(this);
    return new Promise(resolve => {
        if (typeof _maxProgressCount !== 'number' || isNaN(_maxProgressCount) || _maxProgressCount <= 0) {
            resolve();
            return;
        }
        if (_inProgressCount < _maxProgressCount) {
            getConcurrent(this)._inProgressCount += 1;
            resolve();
        } else {
            getConcurrent(this)._waitingArr.push(resolve);
        }
    });
}
interceptors 方法返回一个 Promise。
方法检查 maxProgressCount 是否有效(是正数)。
如果当前进行中的任务数量小于最大并发数,则增加进行中的任务数并解析 Promise。
否则,将 resolve 函数推送到等待任务数组中,等待直到可以处理。
next 方法
next = () => {
    getConcurrent(this)._inProgressCount -= 1;
    const nextResolve = getConcurrent(this)._waitingArr.shift();
    if (nextResolve) {
        getConcurrent(this)._inProgressCount += 1;
        nextResolve();
    }
}
next 方法在任务完成时调用。
减少进行中的任务数,并从等待数组中取出一个 resolve 函数来处理下一个任务。
updateMaxProgressCount 方法
updateMaxProgressCount = (newMaxProgressCount) => {
    getConcurrent(this)._maxProgressCount = newMaxProgressCount;
}
更新最大并发任务数的值。
maxProgressCount 属性
get maxProgressCount() {
    return getConcurrent(this)._maxProgressCount;
}
set maxProgressCount(newVal) {
    this.updateMaxProgressCount(newVal);
}
maxProgressCount 是一个 getter 和 setter。
获取 maxProgressCount 属性的值。
通过 setter 更新最大并发任务数。
inProgressCount 属性
get inProgressCount() {
    return getConcurrent(this)._inProgressCount;
}
set inProgressCount(newVal) {
    throw new Error(`inProgressCount为只读属性`);
}
inProgressCount 是一个 getter 和 setter。
获取进行中的任务数。
setter 抛出错误,因为 inProgressCount 是只读的。
waitingArr 属性
get waitingArr() {
    return getConcurrent(this)._waitingArr;
}
set waitingArr(newVal) {
    throw new Error(`waitingArr为只读属性`);
}
waitingArr 是一个 getter 和 setter。
获取等待中的任务数组。
setter 抛出错误,因为 waitingArr 是只读的。
总结
ConcurrentInterceptors 类用于管理任务的并发执行。它通过 interceptors 方法来处理任务,根据当前的并发任务数决定是否执行新任务或将其放入等待队列。通过 next 方法来启动等待队列中的任务。该类还提供了更新最大并发数的功能,并通过只读属性提供当前并发任务数和等待队列的访问。

// http 封装


"use strict";
import axios from "axios"
import bus from '@/utils/bus.js'
import store from "@/store"
import router from '@/router'
// 并发控制器
import ConcurrentInterceptors from './concurrentInterceptors';
const concurrent = new ConcurrentInterceptors(4) // 最大并发量
import {
  getToken,
  getRefreshExpires,
  getRefreshToken
} from '@/utils/auth'
import {
  refreshToken
} from 'api/api.js'
import { sysAppId } from "@/settings.js"
// import expiresCalculation from "./expires"

axios.interceptors.request.use(
  async config => {
    // config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
    //=========================================================
    // 请求拦截
    const isToken = (config.headers || {}).isToken === false;
    let token = getToken() ? getToken() : "";
    if (token && !isToken) {
      config.headers["Authorization"] = "Bearer " + token
      config.headers["sysappid"] = sysAppId
    }
    await concurrent.interceptors() //并发配置
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

axios.interceptors.response.use(
  response => {
    //响应拦截
    // 当队列中有请求时,执行下一个请求
    concurrent.next() //并发配置
    return response;
  },
  error => {
    concurrent.next() //并发配置
    return Promise.resolve(error.response);
  }
);

const whiteList = [
  "/api/Jwt/token",
  "/api/Jwt/AccessTokenByOracleJwtToken"
]
function getData(val, url) {
  //处理请求数据
  // 判断登录用户信息/权限
  if (whiteList.join(",").indexOf(url) >= 0) {
    // 密码登录:登录    手机登录:获取验证码、登录
    return val
  } else {
    const token = getToken() ? getToken() : ""
    if (!token) {
      bus.$message.warning('用户令牌失效,请重新登录~')
      setTimeout(async ()=>{
        await store.dispatch('user/logout')
        router.push('/login')
      },100)
    }
  }
  if (typeof val == "string") {
    return JSON.parse(val)
  } else {
    return val
  }
}
// 请求参数
function getOptions(url, method, data, params, queryAndBody,otherUrl) {
  const options = {
    method: method,
    baseURL: otherUrl ? otherUrl : window.g.dev && '/' || window.g[`API_ROOT` + (location.search.split('systemcode=')[1] ? ('_' + location.search.split('systemcode=')[1].split('&')[0]) : '')],
    url: url,
    timeout: window.g.AXIOS_TIMEOUT ? window.g.AXIOS_TIMEOUT : 10000,
    headers: {}
  };
  if(data && data.url){
      delete data.url
  }
  if (!!queryAndBody) {
    const paramsData = data;
    const bodyObject = paramsData.bodyObject;
    delete paramsData.bodyObject;
    options.headers['Content-Type'] = 'application/json;charset=UTF-8';
    options.params = getData(paramsData, url);
    options.data = getData(bodyObject, url);
  } else {
    const pd = !params ? "data" : "params";
    options[pd] = getData(data, url);
  }

  return options;
}
function getOptionsJwt(url, method, data, params, queryAndBody) {
  const options = {
    method: method,
    baseURL: window.g.API_ROOT_JWT,
    url: url,
    timeout: window.g.AXIOS_TIMEOUT ? window.g.AXIOS_TIMEOUT : 10000,
    headers: {}
  };
  if (!!queryAndBody) {
    const paramsData = data;
    const bodyObject = paramsData.bodyObject;
    delete paramsData.bodyObject;
    options.headers['Content-Type'] = 'application/json;charset=UTF-8';
    options.params = getData(paramsData, url);
    options.data = getData(bodyObject, url);
  } else {
    const pd = !params ? "data" : "params";
    options[pd] = getData(data, url);
  }

  return options;
}

async function checkStatus(response) {
  //处理响应数据
  if (response && response.status === 200) {
    // loading,如果http状态码正常,则直接返回数据
    let res = response.data;
    if (!res) {
      bus.$message.error("服务器开了个小差");
      return false;
    }
  if (res.code == 10000 || res.Code == 10000 || res.code == 10001 || res.StatusCode === 200 || res.StatusCode === 400 || res.StatusCode === 500) {
    // bus.$message.error(res.message);
    return res;
  }else if(res.StatusCode === 400 || res.StatusCode === 500){
    if(res.message){
        bus.$message.error(res.message);
    }
    if(res.Msg){
        bus.$message.error(res.Msg);
    }
    return res;
    } else {
      if((res.StatusCode == 403 || res.Code == 403 || res.code == 10050) && (res.message === '该令牌已失效' || res.Message === '令牌过期' || res.Msg === '令牌过期')){
        console.log(res.Code == 403, res.Message == '令牌过期')
        const refreshTimeFlag = calcRefreshTime()
        if (refreshTimeFlag) {
          // 用刷新令牌刷新访问令牌并重新发起请求
          const _res = await handleRefreshToken(response)
          return _res
        } else {
          bus.$message.warning(`${res.message || res.Message || res.Msg || '令牌过期'},请重新登录~`)
          setTimeout(async () => {
            await store.dispatch('user/logout')
            router.push('/login')
          }, 100)
        }
        return response
      } else {
        bus.$message.warning(`${res.message || res.Message || res.Msg || '令牌过期'},请重新登录~`)
      }
      return false;
    }
  } else if (response.status === 401) {
    const refreshTimeFlag = calcRefreshTime()
    if (refreshTimeFlag) {
      // 用刷新令牌刷新访问令牌并重新发起请求
      const _res = await handleRefreshToken(response)
      return _res
    } else {
      showMessage('warning',`${response.data.message || response.data.Message || response.data.Msg || '令牌过期'},请重新登录~`)
      setTimeout(async () => {
        await store.dispatch('user/logout')
        router.push('/login')
      }, 100)
    }
    return response
  } else {
    if(response.data.code == 10040 && response.data.message == '该令牌已失效'){
      bus.$message.error(response.data.message+'~ 请重新登录!');
      setTimeout(async ()=>{
        await store.dispatch('user/logout')
        router.push('/login')
      }, 100)
    }if(response.data.code == 403 && response.data.message == '该令牌已失效' || response.data.message == 'Unauthorized'){
      bus.$message.error(response.data.message+'~ 请重新登录!');
      setTimeout(async ()=>{
        await store.dispatch('user/logout')
        router.push('/login')
      }, 100)
    }else if (response.data.Message) {
      bus.$message.error(response.data.Message || response.data.Msg);
    } else if (response.data.message) {
      bus.$message.error(response.data.message  || response.data.Msg);
    }
    return false;
  }
}

// 计算判断刷新令牌是否过期
function calcRefreshTime() {
  const refreshTime = getRefreshExpires()
  const nTime = new Date().getTime()
  const nTimeSecond = Math.floor(nTime / 1000)
  const expTime = refreshTime - nTimeSecond
  if (expTime < 10) {
    return false
  }
  return true
}
export async function handleRefreshToken(response) {
  // 如果能走到这一步,说明客户端时间已经判断过,再判断下token
  if (response.data.code === 10050) {
    // 如果有这个字段 去刷新token
    let token = getToken()
    let refreshTokenStr = getRefreshToken() // 刷新令牌
    // console.log('handlerefreshtoken token', token)
    if (!token || !refreshTokenStr || whiteList.indexOf(response.config.url)>=0) {
      // 如果token不存在
      await store.dispatch('user/logout')
      router.push('/login')
    } else {
      let data = {
        accessToken: token,
        refreshToken: refreshTokenStr
      }
      return new Promise(async (resolve, reject) => {
        await refreshToken(data).then(async (res) => {
          if (res) {
            const cont = res.content
            await store.dispatch('user/setNewTokenInfo', cont)
            // 重新调用接口
            resolve(await repeatRequest(response))
          } else {
            console.log('token刷新失败')
            reject()
          }
        }).catch(error => {
          reject(error)
        })
      })
    }
  } else {
    console.log('客户端时间未过期,token过期且无10050')
    // 如果token过期
    bus.$message.warning('用户令牌失效,请重新登录~')
    await store.dispatch('user/logout')
    router.push('/login')
    return false
  }
}

// 令牌时间过期后的刷新令牌方法
export function postRefreshToken() {
  // 如果有这个字段 去刷新token
  let token = getToken()
  let refreshTokenStr = getRefreshToken() // 刷新令牌
  return new Promise(async (resolve) => {
    if (!token || !refreshTokenStr) {
      // 如果token不存在
      resolve(false)
    } else {
      let data = {
        accessToken: token,
        refreshToken: refreshTokenStr
      }
      await refreshToken(data).then(async (res) => {
        if (res) {
          const cont = res.content
          await store.dispatch('user/setNewTokenInfo', cont)
          // 重新调用接口
          resolve(true)
        } else {
          resolve(false)
        }
      }).catch(error => {
        resolve(false)
      })
    }
  })
}

function checkCode(error) {
  bus.$message.error("网络异常~");
}

export default {
  async post(url, data, query, queryAndBody,otherUrl) {
    // await expiresCalculation()
    //   .then(() => { })
    //   .catch(() => {
    //   })
    const options = getOptions(url, "post", data, query, queryAndBody,otherUrl)
    return axios(options)
      .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
  async get(url, data, query,otherUrl) {
    const options = getOptions(url, "get", data, query,false,otherUrl);
    return axios(options)
      .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
  async put(url, data, query, queryAndBody,otherUrl) {
    const options = getOptions(url, "put", data, query, queryAndBody,otherUrl);
    return axios(options)
      .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
  async delete(url, data,query,queryAndBody,otherUrl) {
    const options = getOptions(url, "delete", data, query,queryAndBody,otherUrl);
    return axios(options)
      .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
  async patch(url, data, query,otherUrl) {
    const options = getOptions(url, "patch", data, query,'',otherUrl);
    return axios(options)
      .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
  // JWT
  async postJwt(url, data, query, queryAndBody) {
    const options = getOptionsJwt(url, "post", data, query, queryAndBody)
    return axios(options)
      .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
  async getJwt(url, data, query, queryAndBody) {
    const options = getOptionsJwt(url, "get", data, query, queryAndBody)
    return axios(options)
    .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
  async putJwt(url, data, query, queryAndBody) {
    const options = getOptionsJwt(url, "put", data, query, queryAndBody)
    return axios(options)
      .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
  async deleteJwt(url, data, query) {
    const options = getOptionsJwt(url, "delete", data, query)
    return axios(options)
      .then(response => {
        return checkStatus(response);
      })
      .catch(res => {
        return checkCode(res);
      });
  },
  // 下载
  async getFile(url, data, query) {
    let options = getOptions(url, "get", data, query);
    options.responseType = 'blob'
    return axios(options)
      .then(response => {
        return response
      })
      .catch(res => {
        return res;
      });
  },
}

参考:面试官:假如有几十个请求,如何去控制并发?_w_omit的技术博客_51CTO博客

以下是一个使用Vue进行并发请求优化的代码示例: ```javascript // 在Vue组件中使用axios库进行请求 import axios from 'axios'; export default { data() { return { responseData: [], // 存储每个请求的响应数据 }; }, methods: { async fetchData(url) { try { const response = await axios.get(url); return response.data; } catch (error) { console.error(error); return null; } }, async fetchParallel() { const urls = [ // 这里填写你需要请求的URL列表 'https://example.com/api/1', 'https://example.com/api/2', // ... 'https://example.com/api/100', ]; // 分批并发请求,每次并发10个请求 const batchSize = 10; const batches = Math.ceil(urls.length / batchSize); for (let i = 0; i < batches; i++) { const batchUrls = urls.slice(i * batchSize, (i + 1) * batchSize); const batchRequests = batchUrls.map(url => this.fetchData(url)); const batchResponses = await Promise.all(batchRequests); this.responseData.push(...batchResponses); } console.log(this.responseData); // 输出所有请求的响应数据 }, }, }; ``` 在上述代码中,首先导入了`axios`库用于发送HTTP请求。在`fetchData`方法中,使用`axios.get`发送GET请求,并返回响应数据。 在`fetchParallel`方法中,定义了需要请求的URL列表,然后将URL列表按照每批10个URL进行划分。在每个批次中,使用`Array.map`方法将每个URL传递给`fetchData`方法,得到一个包含所有请求的Promise数组。然后使用`Promise.all`方法等待所有请求完成,并将响应数据追加到`responseData`数组中。 最后,可以通过`console.log`输出所有请求的响应数据。 以上是一个简单的并发请求优化的示例,你可以根据自己的需要进行修改和扩展。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值