请求竞态问题

接口响应顺序与触发顺序不一致,导致后触发的请求被先返回的结果覆盖。

这是典型的请求竞态(Request Race Condition)问题,常见于切换 tab、搜索、下拉选项时短时间内发起多个请求。

✅ 解决方案总结

方案

原理

优点

缺点

1. 请求唯一标识(标记过期)

发请求时保存当前标识,响应回来后比对是否仍有效

简单高效

适用于不需要取消请求的场景

2. AbortController 取消旧请求

切换前调用旧请求的 abort()

能真正取消请求、节省资源

仅支持 fetch / axios 支持较新浏览器

3. 手动维护请求状态

设置 isLoadinglatestRequestId 控制是否更新结果

可控性强

代码稍繁琐

✅ 推荐做法 1:请求唯一标识 + 比对

这是最通用也最简单的方式:

const latestRequestId = useRef(0);

const fetchData = async (tabKey) => {
  const requestId = ++latestRequestId.current; // 每次请求递增
  const res = await api.getData(tabKey);
  if (requestId !== latestRequestId.current) return; // ❌ 已过期请求,忽略

  // ✅ 是最后一次发出的请求,才更新数据
  setData(res.data);
};

✅ 推荐做法 2:使用 AbortController 取消请求(适用于 fetch)

const controllerRef = useRef(null);

const fetchData = async (tabKey) => {
  // 取消之前的请求
  if (controllerRef.current) controllerRef.current.abort();

  const controller = new AbortController();
  controllerRef.current = controller;

  try {
    const res = await fetch(`/api/data?tab=${tabKey}`, {
      signal: controller.signal,
    });
    const json = await res.json();
    setData(json.data);
  } catch (e) {
    if (e.name === 'AbortError') {
      console.log('请求已取消');
    }
  }
};

如果你使用 axios,可以这样配合 CancelToken

// 使用 axios 的 cancelToken
const cancelTokenRef = useRef(null);

const fetchData = async (tabKey) => {
  if (cancelTokenRef.current) {
    cancelTokenRef.current.cancel('取消前一个请求');
  }

  cancelTokenRef.current = axios.CancelToken.source();

  try {
    const res = await axios.get('/api/data', {
      cancelToken: cancelTokenRef.current.token,
      params: { tab: tabKey },
    });
    setData(res.data);
  } catch (err) {
    if (axios.isCancel(err)) {
      console.log('请求取消');
    }
  }
};

✅ 推荐做法 3:封装统一处理 hook

你可以封装一个通用的 hook,比如:

function useSafeRequest(requestFn) {
  const requestId = useRef(0);

  return async (...args) => {
    const id = ++requestId.current;
    const res = await requestFn(...args);
    if (id !== requestId.current) return null;
    return res;
  };
}

✅ 总结推荐

项目类型

推荐方案

React 项目中切换 tab、下拉列表

✅ 请求标识 + 比对(方案 1)最简单

使用 fetch 的新项目

AbortController 取消请求(方案 2)节省资源

axios 项目

CancelToken 取消前一个请求

复杂并发控制场景

✅ 封装统一处理 hook(方案 3)提高可复用性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值