背景
对于后台系统,重复提交是一个特别常见的问题,目前我们的做法是,添加loading,或者是临时根据一个变量来判断是否可以提交,这种方法没有问题,缺点就是需要每次都声明变量,并且容易被开发者忘记,所以我们在想,是否可以添加一个全局的axios拦截,将重复的请求都去掉?
接口取消请求
axios文档中其实由一个专门用于取消请求的工厂方法CancelToken.source,它是基于proposal-cancelable-promises的,需要注意的是,它目前还处于第一阶段,使用方法如下:
const CancelToken = axios.CancelToken;const source = CancelToken.source();axios.get('/user/12345', {cancelToken: source.token}).catch(function(thrown) {if (axios.isCancel(thrown)) {console.log('Request canceled', thrown.message);} else {// 处理错误}});axios.post('/user/12345', {name: 'new name'}, {cancelToken: source.token})// 取消请求(message 参数是可选的)source.cancel('Operation canceled by the user.');
CancelToken.source方法会返回一个对象source,在需要取消的axios请求配置中加入cancelToken:source.token,调用source.cancel()方法即可取消掉该请求
全局配置
在全局维护一个队列,在每次请求的同时,判断一下该队列中是否已经存在相同的请求,如果有,则将当前请求取消掉,如果没有,则正常请求,并且加入到pending队列中,当请求结束后,将该请求从队列中删除即可
| 注意: 1. 对一个请求是否重复的判断,需要你根据项目的实际情况来判断,可能有的接口是相同名字但是参数不同,可能有的请求是前端需要进行循环请求的,它们就都是可以进行重复请求的,所以对于重复的定义,请自行斟酌! 2. cancelToken基于proposal-cancelable-promises,它还处在第一阶段 |
|---|
// axios.jslet pendingQueue = new Map()// 取消需要从全局的axios中拿,如果你用了create,也需要拿导入的axios(随便你命名)let CancelToken = axios.CancelToken;// 拦截axios.interceptors.request.use((config) => {// 可能需要根据传参来判断是否需要进行重复请求校验// if (config.data && !config.data.GLOBAL_CANCEL) {// let flag = judgePendingFuncNew(config);// // 将pending队列中的请求设置为当前// let source = CancelToken.source();// config.cancelToken = source.token;// if (flag) {// // 当前的请求是重复的// source.cancel('取消请求');// } else {// // 当前请求是一个新请求// pendingQueue.set(`${config.method}->${config.url}`, true);// }// }// 请求发起之前先检验该请求是否在队列中,如果在就把队列中pending的请求cancel掉// 此处还可以添加全局的loading,但是需要记住在response中关掉loading,同时在请求失败的catch中也关闭loadinglet flag = judgePendingFuncNew(config);// 将pending队列中的请求设置为当前let source = CancelToken.source();config.cancelToken = source.token;if (flag) {// 当前的请求是重复的source.cancel('取消请求');} else {// 当前请求是一个新请求pendingQueue.set(`${config.method}->${config.url}`, true);}// ...})// 响应axios.interceptors.response.use(response => {removeResolvedFunc(response.config)// ...)}const judgePendingFuncNew = function(config) {if (pendingQueue.has(`${config.method}->${config.url}`)) {return true;}return false;}// 删除队列中对应已执行的请求const removeResolvedFunc = function (config) {if (pendingQueue.has(`${config.method}->${config.url}`)) {pendingQueue.delete(`${config.method}->${config.url}`)}}
注意:取消需要从全局的axios中拿,如果你用了create,也需要拿导入的axios(随便你命名)如下:
// 修改了导入名称import axiosApi from 'axios'window.axios = axiosApi;let pendingQueue = new Map()// 用原本导入的axiosApi而不是后面的axioslet CancelToken = axiosApi.CancelToken;// 响应时间let axios = axiosApi.create({baseURL: ''})
番外
取消接口请求还可以运用到没有权限的情况下,是否有权限,都是通过接口判断的,如果一个页面没有权限,但是请求了多个接口,则会多个无权限提示弹窗,这种情况下,可以把后面的接口取消掉
// axios.jswindow.axios = axiosApi;let source = axiosApi.CancelToken.source();axios.interceptors.request.use((config) => {...if (!config.cancelToken) {config.cancelToken = source.token;}...});axios.interceptors.response.use(response => {...if (response.data.code === 1107) {source.cancel();// 重置tokensource = axiosApi.CancelToken.source();throw new Error(data.data.msg)...})
