前端开发中取消请求的正确姿势与实际踩坑经验分享
直接上代码,取消请求其实没那么难
先说重点,前端取消请求最常见的场景就是用户切换页面或者重复操作时,避免不必要的请求浪费资源。下面是我亲测有效的几种方法,建议直接用。
// Axios CancelToken 的用法
const CancelToken = axios.CancelToken;
let cancel;
axios.get('https://jztheme.com/api/data', {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
});
// 在需要取消的地方调用
cancel('手动取消请求');
上面这段代码看着简单,但实际项目里用起来还是有不少坑的。比如 cancel 函数的调用时机,还有多个请求如何管理的问题。
这个场景最好用:React组件卸载时自动取消
在 React 项目中,我经常遇到一个场景:组件还没加载完就切换了页面,这时候接口返回的数据会报错 “Can’t perform a React state update on an unmounted component”。解决方案如下:
import { useEffect, useState } from 'react';
import axios from 'axios';
function MyComponent() {
const [data, setData] = useState(null);
const source = axios.CancelToken.source();
useEffect(() => {
axios.get('https://jztheme.com/api/data', {
cancelToken: source.token
}).then(res => {
// 防止组件已卸载时更新状态
if (!source.isCancelled()) {
setData(res.data);
}
}).catch(err => {
if (!axios.isCancel(err)) {
console.error(err);
}
});
return () => {
source.cancel('组件卸载,取消请求');
};
}, []);
return <div>{data ? JSON.stringify(data) : '加载中...'}</div>;
}
这里有个小细节要注意:isCancelled 方法其实是我自己扩展的一个判断函数,因为 axios 本身没有提供这样的方法。实现很简单:
axios.CancelToken.source().isCancelled = function() {
return this.reason !== undefined;
};
踩坑提醒:这三点一定注意
1. 别忘了处理并发请求的情况。如果你有多个请求,建议用 Map 来管理 CancelToken:
const requestMap = new Map();
function addRequest(key, cancelFunc) {
requestMap.set(key, cancelFunc);
}
function cancelRequest(key) {
if (requestMap.has(key)) {
requestMap.get(key)('取消特定请求');
requestMap.delete(key);
}
}
2. 不要滥用取消功能。有些同学喜欢给每个请求都加取消,其实没必要。像登录、支付这种关键请求就别取消了。
3. 谨慎处理取消后的状态。特别是当你的 UI 状态依赖于请求结果时,取消后要有相应的 fallback 处理。
高级技巧:封装通用的请求管理器
在大型项目里,我更倾向于封装一个统一的请求管理器,这样可以更好地控制所有请求:
class RequestManager {
constructor() {
this.queue = new Map();
}
add(config) {
const CancelToken = axios.CancelToken;
config.cancelToken = new CancelToken(c => {
this.queue.set(config.url, c);
});
return config;
}
remove(url) {
this.queue.delete(url);
}
cancelAll() {
for (const [url, cancel] of this.queue.entries()) {
cancel(取消请求: ${url});
}
this.queue.clear();
}
cancel(url) {
if (this.queue.has(url)) {
const cancel = this.queue.get(url);
cancel(取消请求: ${url});
this.queue.delete(url);
}
}
}
// 使用示例
const manager = new RequestManager();
axios.interceptors.request.use(config => manager.add(config));
// 在路由切换时
manager.cancelAll();
这个方案虽然看起来复杂点,但在实际项目中特别实用。尤其是单页应用,页面切换时可以一键取消所有进行中的请求。
补充:AbortController 是个好东西
其实现代浏览器已经提供了原生的 AbortController API,比 axios 自带的 CancelToken 更优雅:
const controller = new AbortController();
const signal = controller.signal;
fetch('https://jztheme.com/api/data', { signal })
.then(res => res.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('请求已中止');
}
});
// 取消请求
controller.abort();
这个方式兼容性也不错,主流浏览器基本都支持。而且语法更简洁,推荐新项目使用这种方式。
结尾唠叨几句
以上就是我在实际项目中总结的一些取消请求的经验。从最初的到处写 try-catch,到现在封装统一的请求管理器,踩过的坑真不少。记得有一次忘记处理取消后的 loading 状态,导致整个页面都卡在 loading 状态,被产品经理追着骂。
这个技术的拓展用法还有很多,比如结合 Redux 管理请求状态,或者在 GraphQL 中实现更细粒度的请求控制。这些内容我后续会继续分享。
以上是我个人对取消请求的完整讲解,有更优的实现方式欢迎评论区交流。

暂无评论