前提准备
首先编写一个测试接口,模拟接口延迟返回,请求5秒后再返回结果
使用 node + koa
来实现
const Router = require("koa-router");
const router = new Router();
router.get("/getLongData", async (ctx, res) => {
// 获取get请求的参数
// console.log(ctx.query);
await new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 5000);
});
ctx.body = {
code: 200,
message: "请求成功!",
};
});
module.exports = router;
AbortController
使用 AbortController
AbortController
是一个浏览器提供的 API,用于取消正在进行的异步操作,如 Fetch 请求或 Axios 请求。你可以创建一个 AbortController
实例,并在 Axios 请求配置中通过 signal 属性传递它。
import axios from 'axios';
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(response => {
// 处理响应
}).catch(error => {
if (error.name === 'AbortError') {
console.log('请求被取消');
} else {
console.error('发生了一个错误:', error);
}
});
// 取消请求
controller.abort();
CancelToken
CancelToken
是 Axios 自带的一个类,用于实现请求取消功能。你需要创建一个 CancelToken
实例,并在请求配置中通过 cancelToken 属性传递它。
import axios from 'axios';
import CancelToken from 'axios/cancelToken';
let cancel;
// 发起请求
axios.get('/foo/bar', {
cancelToken: new CancelToken(c => cancel = c)
}).then(response => {
// 处理响应
}).catch(error => {
if (axios.isCancel(error)) {
console.log('请求被取消');
} else {
console.error('发生了一个错误:', error);
}
});
// 取消请求
if (cancel) {
cancel('取消请求的原因');
}
全局取消请求封装
新建一个 globalCancelToken.js
// 最新的一个请求
let cancel = null
// 所有请求
let cancelTokenList = []
function setToken(cancelToken) {
cancel = cancelToken
cancelTokenList.push(cancelToken)
}
function cancelToken() {
cancel && cancel()
cancelTokenList.pop()
}
function clearAllToken() {
while (cancelTokenList.length > 0) {
let cancel = cancelTokenList.pop()
console.log(cancel, 'cancel')
cancel && cancel()
}
}
export {
setToken,
cancelToken,
clearAllToken,
}
这个文件中定义了一个变量,一个数组,cancelTokenList 用于存放每一次请求所对应的 cancelToken
在请求拦截器中添加取消请求的配置,相应拦截器中判断错误类型是否为取消请求
import { setToken } from '@/utils/globalCancelToken.js'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: import.meta.env.VITE_APP_BASE_API,
// 超时
timeout: 100000,
})
// request拦截器
service.interceptors.request.use(config => {
// 省略其他配置....
// 添加可取消请求配置
config.cancelToken = new axios.CancelToken(c => setToken(c))
return config
}, error => {
console.log(error)
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || '0'
// 获取错误信息
const msg = errorCode[code] || res.data.message || errorCode['default']
// 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === 401 || code === '10006') {
if (!isRelogin.show) {
isRelogin.show = true
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
isRelogin.show = false
useUserStore().logOut().then(() => {
location.href = '/index'
})
}).catch(() => {
isRelogin.show = false
})
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
ElMessage({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code !== '0') {
ElMessage({ message: msg, type: 'error' })
return Promise.reject('error')
} else {
return Promise.resolve(res.data)
}
},
error => {
if (error.name === 'CanceledError') {
console.log('请求已取消')
return Promise.reject('请求已取消')
}
ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
},
)
编写测试页面
<template>
<div>
<el-button type='primary' @click='send'>发起请求</el-button>
<el-button type='warning' @click='cancelSend'>取消最近一次请求</el-button>
<el-button type='warning' @click='cancelAllSend'>取消所有请求</el-button>
</div>
</template>
<script setup>
import { testAxiosServer } from '@/api/testApi.js'
import { cancelToken, clearAllToken } from '@/utils/globalCancelToken.js'
function send() {
testAxiosServer().then(res => {
console.log(res)
})
}
function cancelSend() {
cancelToken()
}
function cancelAllSend() {
clearAllToken()
}
// 组件销毁前取消所有未完成的请求
onUnmounted(() => {
clearAllToken()
})
</script>