超时处理实战经验分享:前端开发中的那些坑与解决方案

星瑶 优化 阅读 2,470
赞 60 收藏
二维码
手机扫码查看
反馈

项目初期的技术选型

最近我们团队接了一个新项目,是个电商网站。本来以为就是个常规的前端活儿,没想到一上来就遇到了不少问题。其中一个比较头疼的问题就是请求超时处理。

超时处理实战经验分享:前端开发中的那些坑与解决方案

在做这个项目之前,我一直觉得超时处理没啥大不了的,毕竟不就是加个timeout嘛。结果真正开始做了才发现,这事儿还真没那么简单。

开始没想到:超时处理的复杂性

刚开始的时候,我简单地在每个请求里加了个timeout配置,以为这样就能搞定一切。结果上线后用户反馈一堆,说有时候请求半天没反应,页面卡得不行。这才意识到,超时处理不仅仅是设置一个时间那么简单。

我开始上网查资料,发现了不少关于超时处理的最佳实践。然后就开始尝试各种方案,希望能找到一个靠谱的解决方案。

核心代码就这几行

经过一番折腾,最后我决定采用Promise.race的方式来处理超时。这种方式可以很好地控制请求的超时时间,并且代码也比较简洁。

这里是我最终实现的代码:

const fetchWithTimeout = (url, options, timeout = 5000) => {
  const controller = new AbortController();
  const { signal } = controller;

  const timeoutId = setTimeout(() => {
    controller.abort();
    console.error('请求超时');
  }, timeout);

  return fetch(url, { ...options, signal })
    .then(response => {
      clearTimeout(timeoutId);
      return response;
    })
    .catch(error => {
      if (error.name === 'AbortError') {
        console.error('请求被取消');
      }
      throw error;
    });
};

// 使用示例
fetchWithTimeout('https://jztheme.com/api/data', { method: 'GET' })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

这段代码的核心就是在fetch请求中使用AbortController来控制请求的取消。通过Promise.race的方式,我们可以设置一个定时器,如果请求在设定的时间内没有完成,就会触发abort事件,从而取消请求。

最大的坑:性能问题

虽然上面的代码看起来挺不错的,但实际运行起来还是有些问题。首先是性能问题,我发现每次请求都会创建一个新的AbortController实例,感觉有点浪费资源。后来调整了方案,改成全局的AbortController实例,但这又引入了新的问题,比如不同请求之间的干扰。

为了解决这个问题,我引入了一个Map来存储每个请求的AbortController实例,确保每个请求都有独立的控制。改完后的代码如下:

const abortControllers = new Map();

const fetchWithTimeout = (url, options, timeout = 5000) => {
  const controller = new AbortController();
  const { signal } = controller;
  const key = ${url}-${JSON.stringify(options)};

  abortControllers.set(key, controller);

  const timeoutId = setTimeout(() => {
    controller.abort();
    console.error('请求超时');
    abortControllers.delete(key);
  }, timeout);

  return fetch(url, { ...options, signal })
    .then(response => {
      clearTimeout(timeoutId);
      abortControllers.delete(key);
      return response;
    })
    .catch(error => {
      if (error.name === 'AbortError') {
        console.error('请求被取消');
      }
      abortControllers.delete(key);
      throw error;
    });
};

// 使用示例
fetchWithTimeout('https://jztheme.com/api/data', { method: 'GET' })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

这样,每个请求都有自己独立的AbortController实例,并且在请求完成后会从Map中删除对应的实例,避免了内存泄漏的问题。

回顾与反思

经过这一番折腾,总算把超时处理的问题解决了。虽然还有一些小问题没有完全解决(比如某些极端情况下请求还是会卡住),但总体来说已经达到了预期的效果。

通过这次经历,我深刻体会到超时处理的重要性。不仅仅是为了提升用户体验,更是为了保证系统的稳定性和可靠性。希望我的这些经验对你也有帮助,如果你有更好的解决方案,欢迎在评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论
UX永莲
UX永莲 Lv1
文章里的内容很有深度,每一次阅读都能有新的收获,值得反复品味。
点赞 6
2026-02-11 11:25