一、取消Axios的发送的两种方式;
1、使用AbortController的方法取消发送;
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(res=> {
//...
}).catch(err=>{
console.log(err); // 打印错误消息
});
// 取消请求
controller.abort()
这个很容易理解,就是new一个AbortController的方法,然后把它作为参数,直接在参数中传递signal字段,我们想要取消的时候,就直接调用其中的abort方法就可以了。注意,此时的请求会直接进catch方法,而不会走then
2、使用CancelToken的方法取消发送(新版本不应该使用此方法)
这个方法在官方的文档中明确的表示了,在v0.22.0版本之后,不应该使用此方法了。我看了一下我的axiso的版本,现在已经是v1.7.5版本了(2024年8月29日),所以不应该使用此方法了。
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).then(res=>{
}).catch(thrown =>{
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
使用方法和上面基本上一样。因为在新版本中已经不支持了,所以再次也不赘述了;
二、已经封装了Axios的方法,取消调用的方式;
但是在实际的项目中,大家很少会和上面一样,直接使用axios发送请求,都是对axios进行二次封装。有请求拦截器,还有返回拦截器,统一错误处理等方法;
比如下面的方式:
import axios from "axios";
const $http = axios.create({
baseURL: process.env.NODE_ENV == "development" ? 'http://localhost:8848/':'/',
timeout: process.env.NODE_ENV == "development" ? 5 * 60 * 1000 : 5 * 60 * 1000 // 超时时间都改成十分钟,因为重启的接口,需要时间较长
})
// 请求拦截器
$http.interceptors.request.use(
config => {
// 如果传输的参数中,有baseURL字段,需要覆盖接口的url
// 说明此接口请求地址不是默认的
if(config.data.hasOwnProperty('baseURL') ){
config.baseURL = config.data.baseURL;
delete config.data.baseURL;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截器
$http.interceptors.response.use(
response => {
const res = response.data;
if (res.code != 200) {
return Promise.reject("res.des"); // 直接调用catch方法,报错;
} else {
return res;
}
},
error => {
// console.log('error' + error) // for debug
return Promise.reject(error)
}
)
export default $http;
这里封装了一个$http的方法,然后我们的接口文件是在另一个api.js文件中,调用此方法;
import $http from "../request/index";
// 查找右侧所有的站点
export function findApp(params) {
params.cmd = "findApp";
return $http({
url: "/ms/api",
method: "post",
contentType: "application/json",
data: params,
});
}
当我们想要调用findApp方法的时候,就需要按照下面的方式:
import { findApp } from "../api/index"; // 各个接口
findApp({
baseURL: `http://${ip}:${port}`
}).then(res=>{
// 其他的逻辑操作
}).catch(err=>{
ELMessage.error(err); // 提示错误信息
})
三、取消之后,再次发送的正确方式;
因为看到官方的示例,所以第一想法是把取消发送的方法,写进了对于axios的封装中:
以下的代码是不正确的
import axios from "axios";
const controller = new AbortController();
const $http = axios.create({
baseURL: process.env.NODE_ENV == "development" ? 'http://localhost:8848/':'/',
timeout: process.env.NODE_ENV == "development" ? 5 * 60 * 1000 : 5 * 60 * 1000
})
// 请求拦截器
$http.interceptors.request.use(
config => {
// console.log(config);
if(config.data.hasOwnProperty('baseURL') ){
config.baseURL = config.data.baseURL;
delete config.data.baseURL;
}
config.signal = controller.signal; // 添加取消的标识
if(config.method == "post" && config.contentType ){
config.data = {
...config.data, // 让原来的覆盖掉通用的
}
}
return config;
},
error => {
return Promise.reject(error);
}
);
这里的问题是,如果我把 这个标识符写进公共的封装的这个组件中,导致了其他的接口再次发送的时候,也会被取消,因为他们都是用的这一个实例。所以导致了,这个接口取消了,再次调用的时候,发现这个接口直接就取消了,不能再次调用了。
所以要是想要只取消一个接口一次发送请求,需要在调用接口的时候,给当前需要取消的接口发送标识,而不是在公共中封装的代码中发送标识;
四、正确代码示例
1、添加标志位
首先在能取消发送的接口中添加一个标志位。方便后续的程序处理;
export function findApp(params) {
params.canCancel = "true";
params.cmd = "findApp";
return $http({
url: "/ms/api",
method: "post",
contentType: "application/json",
data: params,
});
}
2、封装方法中处理
在封装的方法中,添加对于canCancel的处理,因为传过来的参数都是直接放进data中,需要把这个参数放在外面;
import axios from "axios";
const $http = axios.create({
baseURL: process.env.NODE_ENV == "development" ? 'http://localhost:8848/':'/',
timeout: process.env.NODE_ENV == "development" ? 5 * 60 * 1000 : 5 * 60 * 1000 // 超时时间都改成十分钟,因为重启的接口,需要时间较长
})
// 请求拦截器
$http.interceptors.request.use(
config => {
// console.log(config);
// 如果传输的参数中,有baseURL的话,就会覆盖接口的url
if(config.data.hasOwnProperty('baseURL') ){
config.baseURL = config.data.baseURL;
delete config.data.baseURL;
} else if(config.hasOwnProperty('canCancel')) {
config.signal = config.data.signal;
delete config.data.signal;
delete config.canCancel;
}
if(config.method == "post" && config.contentType ){
config.data = {
...config.data, // 让原来的覆盖掉通用的
}
}
return config;
},
error => {
return Promise.reject(error);
}
);
3、接口中添加参数
var controller = new AbortController();
findApp({
signal: controller.signal
}).then((res) => {
// 处理业务逻辑
}).catch(err=>{
console.log("打印错误信息", err);
})
// 如果想要取消当前的接口的方法,直接调用
const canCancelClick = () => {
concatController.abort();
}
五、其他的注意事项
需要额外的注意的一点就是,取消axios发送接口,一般都是用于前端查询类的接口。当页面跳转了,或者进行其他的操作了。当前的接口没用了,就能给取消了。
但是如果是上传数据类的接口,比方说是上传文件的接口,如果只是单纯的前端取消接口的话,可能会导致,前端这边已经取消了,但是接口在后台已经处理成功了。会导致前后端数据不统一;这个时候就需要额外的操作了。
最后附上一个官方的中文文档