【业务需求系列】取消axios请求

今天给大家分享一篇实际业务中经常会遇到的问题,关于如何取消已发出的请求。

需求场景

在可视化搭建项目中,一个页面中含有多个图片上传组件是很普遍的场景,每一个图片组件都有各自的上传接口,当上传图片的时候 ,会出现第一个图片组件上传的结果返回至第二个图片模块中的bug。

还有一些很常见的场景,就比如我们连续多次获取列表数据,但第一次的请求却在最后才返回,导致列表展示数据不是最新。

类似的场景还有,在vue单页面开发中 ,页面的切换后,上一个页面的请求还没完成,此时不仅会影响页面性能,还可能会对页面的数据展示有一定的影响。

需求分析

以上面提到的图片上传组件为例,导致图片的返回结果与预期顺序不符的主要原因是:接口返回是异步的 。当目标模块变换之后,上传接口才返回,此时会导致上一次的请求结果,落在了当前目标模块中。

此外面试的时候也会遇到类似的场景题:请求已经发出去了,如何取消?

解决方案

中断请求的原理

首先需要清楚,axios底层调用的是·XMLHttpRequest,而XMLHttpRequest提供了 abort() 方法用于终止请求。

中断请求的底层具体实现是:如果该请求已被发出,XMLHttpRequest.abort() 方法将终止该请求。当一个请求被终止,它的 readyState 将被置为 XMLHttpRequest.UNSENT(0),并且请求的 status置为 0。

readyState属性变为 4 意味着请求已经完成,客户端不会再等待服务端的返回了,status属性变为0

具体实现方案

  • 方法1:使用cancelToken.sourse工厂方法创建cancel token
const CancelToken = axios.CancelToken;
let cancel;

axios.get('请求路径', {
  cancelTokennew CancelToken(function executor(c{
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});

// cancel the request
cancel();
  • 方法2:通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:
const CancelToken = axios.CancelToken;
let cancel;

axios.get('请求路径', {
  cancelTokennew CancelToken(function executor(c{
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});

// cancel the request
cancel();
  • vue中也可以这样解决
//直接在 API里面引入store(vuex文件),
// 把请求实例存到vuex里面,
// 在需要终止请求的时候直接调用实例方法就行了()

import request from '@/request'
import axios from 'axios'
import store from 'store'
var CancelToken = axios.CancelToken;
export function cancel(params:any{
  return request({
    url'路径',
    method'get',
    params,
    cancelTokennew CancelToken(function executor(c:any{
      store.commit('test',c)
    })
  })
}


// 在需要调用的页面
// 假如在 vuex里面的存值 cancel
// 在要中断请求的页面取到vuex的值直接写
cancel()
  • 再补充一个切换页面的中断请求的方案
// 重新封装axios请求,在router.beforeEach强制中断请求
Vue.prototype.$http= axios;
//Vue函数添加一个原型属性$axios 指向axios,
// 这样vue实例或组件中不用再去重复引用Axios  
// 直接用this.$axios就能执行axios 方法
const CancelToken = axios.CancelToken;
Vue.$httpRequestList=[];

Vue.prototype.$ajax = (type, url, data) => {
    return new Promise((resolve, reject) => {   //封装ajax
        var aa = {
            method: type,
            url: url,
            cancelTokennew CancelToken(c => {  
            //强行中断请求要用到的
                Vue.$httpRequestList.push(c);
            })
        }
        var json = (type == 'get') ? 
        Object.assign(aa, { params: data }) : 
        Object.assign(aa, { data: data });
        var ajax = Vue.prototype.$http(json).then(res => {
            resolve(res);
        }).catch(error => {   //中断请求和请求出错的处理
                if (error.message == "interrupt") {
                    console.log('已中断请求');
                    return;
                } else {
                    reject(error);
                }
            })
        return ajax;
    })
};

router.beforeEach((to, from, next) => {   
//路由切换检测是否强行中断,
    if(Vue.$httpRequestList.length>0){        
    //强行中断时才向下执行
        Vue.$httpRequestList.forEach(item=>{
            item('interrupt');
            //给个标志,中断请求
        })  
    }
    next();    
});

// 使用方法
this.$axios('get',url,param).then(res=>{}).catch(err=>{});

最后

我们推出简历辅导,模拟面试已经有一个月了,已经陆续为20多位小伙伴完成了服务,也得到大家的认可和支持。其中也收到了一些小伙伴拿到心仪offer的喜报,我们同样也为大家感到高兴~

在为大家提供优质服务提高面试通过率的同时,我们的几位导师同样付出了很大的心血和时间成本,我们的目的就是为了让大家花费每一分钱都物有所值。

鉴于目前小伙伴们的积极支持和我们导师的有限精力和时间成本,以及目前已经预约排队准备服务的小伙伴时间逐渐延长,我们决定从 「4月11日0时起」,增值服务价格 全面上调「50元/次」 。我们的几位导师也将继续努力为大家提供更优质的简历辅导和模拟面试服务。帮助大家在前端面试道路上少走弯路,增加面试成功率~

在4月11日之前,我们还继续保持目前的「优惠价格」,如果大家近期有需要可以尽快联系小助手(interview-fe)进行预约哦。