You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Vite中使用Axios解决ConcurrencyViolation错误的方法

429请求过多错误(ConcurrencyViolation)解决方案

问题核心

你遇到的429 (Too Many Requests)错误,对应ConcurrencyViolation错误码和Max concurrency allowed 1 per 1 seconds提示,意思是接口限制每秒只能发起1个并发请求。你之前的尝试没解决问题,核心原因在于:

  • 多个useFetch钩子实例各自为政,没共享全局的限流规则
  • 手动sleep完全无效——多个请求的等待是并行执行的,到时间后还是会同时发起请求
  • 之前用p-limit时,每个钩子都创建了独立的限制器,等于没做全局限流

可行解决方案

1. 全局共享请求限制器

用p-limit必须保证所有请求都使用同一个限制实例,这样才能实现全局的1次/秒并发控制:

第一步:创建全局限制器文件

新建requestLimiter.js:

import pLimit from 'p-limit';
// 严格匹配接口要求:每秒最多1个请求
const requestLimit = pLimit(1);
export default requestLimit;

第二步:修改useFetch钩子

把请求逻辑用全局限制器包裹,同时移除无效的sleep代码:

import requestLimit from './requestLimiter';
import axios from 'axios';
import { useEffect, useRef, useState } from 'react';

export default function useFetch(url, options) {
  const [news, setNews] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const isMounted = useRef(true);

  useEffect(() => {
    setLoading(true);

    // 用全局限制器包裹请求逻辑
    const fetchTask = requestLimit(async () => {
      if (!isMounted.current) return;
      try {
        const res = await axios.request({ ...options, url });
        if (isMounted.current) {
          setNews(res.data.articles);
        }
      } catch (err) {
        if (!isMounted.current) return;
        if (err.response) {
          setError(err.response.data);
        } else if (err.request) {
          console.log(err.request);
        } else {
          console.log(`Error: ${err.message}`);
        }
      } finally {
        if (isMounted.current) {
          setLoading(false);
        }
      }
    });

    fetchTask();

    // 组件卸载时标记状态,避免更新已卸载的组件
    return () => {
      isMounted.current = false;
    };
  }, [url, options]); // 注意将options加入依赖数组

  return { news, loading, error };
}

2. 额外优化:添加请求取消机制

为了避免组件卸载或url/options变化后,旧请求依然执行导致的无效请求,可以给axios添加取消令牌:

import requestLimit from './requestLimiter';
import axios from 'axios';
import { useEffect, useRef, useState } from 'react';

export default function useFetch(url, options) {
  const [news, setNews] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const isMounted = useRef(true);
  const cancelTokenSource = useRef(null);

  useEffect(() => {
    setLoading(true);
    // 创建取消令牌
    cancelTokenSource.current = axios.CancelToken.source();

    const fetchTask = requestLimit(async () => {
      if (!isMounted.current) return;
      try {
        const res = await axios.request({
          ...options,
          url,
          cancelToken: cancelTokenSource.current.token
        });
        if (isMounted.current) {
          setNews(res.data.articles);
        }
      } catch (err) {
        if (axios.isCancel(err)) {
          console.log('请求已取消:', err.message);
          return;
        }
        if (!isMounted.current) return;
        if (err.response) {
          setError(err.response.data);
        } else if (err.request) {
          console.log(err.request);
        } else {
          console.log(`Error: ${err.message}`);
        }
      } finally {
        if (isMounted.current) {
          setLoading(false);
        }
      }
    });

    fetchTask();

    return () => {
      isMounted.current = false;
      // 取消未完成的请求
      cancelTokenSource.current?.cancel('因组件卸载或参数变化取消请求');
    };
  }, [url, options]);

  return { news, loading, error };
}

为什么之前的尝试失败?

  • p-limit如果每个钩子都单独创建限制器,每个钩子的请求都会在自己的队列里执行,多个钩子同时发起请求还是会突破并发限制,必须全局共享一个限制器
  • xhr、cross-fetch只是替换了请求库,没解决并发限流的核心问题
  • 手动sleep是每个请求独立等待,多个请求的等待过程是并行的,到时间后依然会同时发起请求,完全起不到限流作用

内容的提问来源于stack exchange,提问作者user

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 18:10:54