axios取消请求总结

7401次阅读 510人点赞 作者: WuBin 发布时间: 2022-10-13 15:01:34
扫码到手机查看

实测可行AbortController取消请求

v0.22.0开始,Axios 支持以 fetch API 方式——AbortController取消请求:

const controller = new AbortController();
 
axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// 取消请求
controller.abort()
当使用AbortController,中断请求的时候,会触发catch,这时候需要使用axios.isCancel()方法判断是否是人为的终止请求。

具体使用可以参考我封装的一个函数:《基于axios封装简单的基本请求函数》

使用此方法简单快捷,如果有次需求可以直接使用此方法终止请求。

以下是浏览器支持情况,在手机端可以放心大胆的用。

https://caniuse.com/?search=AbortController

如何取消请求

取消请求在前端有时候会用到,以下是两个工作中可能会用到的场景

  1. tab切换时刷新某个列表数据,如果他们共用一个变量存储数据列表,当请求有延时,可能会导致两个tab数据错乱;
  2. 导出文件或下载文件时,中途取消 。

取消http请求,axios文档里提供了两种用法:

第一种:使用 CancelToken

const { CancelToken, isCanCel } = axios;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(thrown => {
  if (isCancel(thrown)) {
    // 获取 取消请求 的相关信息
    console.log('Request canceled', thrown.message);
  } else {
    // 处理其他异常
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// 取消请求。参数是可选的,参数传递一个取消请求的相关信息,在 catch 钩子函数里能获取到
source.cancel('Operation canceled by the user.');

第二种

给构造函数 CancelToken 传递一个 executor 函数作为参数。这种方法的好处是,可以用同一个 cancel token 来取消多个请求

const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // 参数 c 也是个函数
    cancel = c;
  })
});
// 取消请求,参数用法同上
cancel();

项目中用法示例

在一个真实的项目中,一般都会对axios进行二次封装,针对请求、响应、状态码、code等做处理。贴一个项目里常用的request.js:

import axios from 'axios'
import store from '@/store'
import { getToken } from '@/utils/auth'

// 创建一个 axios 实例,并改变默认配置
const service = axios.create({
  baseURL: process.env.BASE_API, // api 的 base_url
  timeout: 5000 // request timeout
})

// 请求拦截
service.interceptors.request.use(
  config => {
    // Do something before request is sent
    if (store.getters.token) {
      // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
      config.headers['X-Token'] = getToken()
    }
    return config
  },
  error => {
    // Do something with request error
    console.log(error) // for debug
    Promise.reject(error)
  }
)

// 响应拦截
service.interceptors.response.use(
  response => response,
  error => {
    alert(error)
    return Promise.reject(error)
  }
)

export default service

对于某一个请求添加取消的功能,要在调用api时,加上cancelToken选项,使用时的示例:

// api.js
import request from 'request'
export function getUsers(page, options) {
  return request({
    url: 'api/users',
    params: {
      page
    },
    ...options
  })
}
// User.vue
import { CancelToken, isCancel } from 'axios'
import { getUsers } from 'api'

...

cancel: null

...

toCancel() {
  this.cancel('取消请求')
}

getUsers(1,
  {
    cancelToken:  new CancelToken(c => (this.cancel = c))
  }
)
.then(...)
.catch(err => {
  if (isCancel) {
    console.log(err.message)
  } else {
    ...
  }
})

以上,我们就可以顺顺利利地使用封装过的axios,取消某一个请求了。其原理无非就是把cancelToken的配置项,在调用api时加上,然后就可以在业务代码取消特定请求了。

批量取消请求

在 document 里的第二种方法已经说过:通过指定同一个cancel token来取消。但是,在上面的项目示例中,不能控制拿到相同的cancel token。我们可以换个思路:用数组保存每个需要取消的cancel token,然后逐一调用数组里的每一项即可:

// User.vue
import { CancelToken, isCancel } from 'axios'
import { getUsers } from 'api'

...

cancel: []

...

toCancel() {
  while (this.cancel.length > 0) {
    this.cancel.pop()('取消请求')
  }
}

getUser1(1,
  {
    cancelToken:  new CancelToken(c1 => (this.cancel.push(c1)))
  }
)

getUser2(2,
 {
  cancelTokem: new CancleTokem(c2 => (this.cancel.push(c2)))
 }
)

切换路由时,取消请求

上面讲了取消一个请求及页面内批量abort的方法,此外,还有一种需求——切换路由时,取消所有。 这里不详细赘述了,大概思路就是在请求拦截器里,统一加个token,并设置全局变量source控制一个cancel token,在路由变化时调用cancel方法。

http.interceptors.request.use(config => {
    config.cancelToken = store.source.token
    return config
}, err => {
    return Promise.reject(err)
})

router.beforeEach((to, from, next) => {
    const CancelToken = axios.CancelToken
    store.source.cancel && store.source.cancel()
    store.source = CancelToken.source()
    next()
})

// 全局变量
store = {
    source: {
        token: null,
        cancel: null
  }
}

取消请求的实现原理

cancelToken的source方法维护了一个对象,里面包括了token令牌和cancel方法,token来自与构造函数CancelToken,调用cancel方法后,token的promise状态为resolved,进而又调用了xhr的abort方法,取消请求成功。 来分析下取消请求是怎么实现的,先从一个简单的取消请求的例子开始:

var CancelToken = axios.CancelToken;
var source = CancelToken.source();
axios.get('/get?name=xmz', {
    cancelToken : source.token
}).then((response)=>{
    console.log('response', response)
}).catch((error)=>{
    if(axios.isCancel(error)){
        console.log('取消请求传递的消息', error.message)
    }else{
        console.log('error', error)
    }
})
// 取消请求
source.cancel('取消请求传递这条消息');

这就是一个简单的取消请求的例子,那么就从最开始的axios.CancelToken来看,先去axios/lib/axios.js文件中。

axios.CancelToken = require('./cancel/CancelToken');

不费吹灰之力,就找到了CancelToken,在例子中我们调用了source方法,那么就去axios/lib/cancel/CancelToken.js文件中看看这个source方法到底是干什么的?

CancelToken.source = function(){
    var cancel;
    var token = new CancelToken(function executor(c) {
        cancel = c
    })
    return {
        token : token,
        cancel : cancel
    }
}

source方法很简单,就是返回一个具有token和cancel属性的对象,但是token和cancel都是通过CancelToken这个构造函数来的,那么还在这个文件中向上看,找到CancelToken函数。

function CancelToken (executor){
    // ...
    // 判断executor是一个函数,不然就报错
    var resolvePromise;
    this.promise = new Promise(function(resolve){
        resolvePromise = resolve;
    })
    var token = this;
    // 以上token现在有一个promise属性,是一个未成功的promise对象;
    executor(function cancel(message){
        if(token.reason){
            return;
        }
        token.reason = new Cancel(message);
        resolvePromise(token.reason);
    })
    // 这个cancel函数就是 上面函数中的cancel,也就是source.cancel;
}

现在知道了source.cancel是一个函数,souce.token是一个实例化对象,暂时就知道这些,继续看文章最开始的例子,接下来是去发送请求了,最下面还有一行代码是执行souce.cancel();souce.cancel就是用来触发取消请求的函数。 现在再回头来看,上面的cancel函数,cancel执行,给token加了一个reason属性,那么看下这个reason属性是什么吧,看下这个Cancel构造函数,在axios/lib/cancel/Cancel.js文件中

function Cancel(message){
    this.message = message
}

Cancel特别简单就是给实例化对象添加一个message属性,所以现在token.reason是一个具有message属性的对象了。 继续回到cancel函数中,resolvePromise函数执行了,那么token.promise对象,这个原本未变成,成功状态的promise,变成了成功状态了,并且将token.reason对象传递过去了。 简单总结一下,执行取消函数,就是让token的promise的状态变成了成功; 好了,突然发现分析中断了,变成成功状态又怎样了,怎么取消的呢?虽然现在的同步代码都执行完了,但是请求还没发送出去呢,我们还要去看发送请求的函数

在分析发送请求之前,再看下最开始的例子,和最普通的发送一个get请求还是有一点区别的,配置对象中多了,一个cancelToken的属性,值是token,到底起了什么作用呢,去axios/lib/adapters/xhr.js中一探究竟(这里只截取其中关于cancelToken的部分)。

// 在发送请求之前,验证了cancelToken,看来此处就是用来取消请求的;
if(config.cancelToken){
    // 具体是如何取消的,是在这个判断内定义的;
    config.cancelToken.promise.then(function(cancel){
        request.abort();
        reject(cancel);
        request = null;
    })
}
// 发送请求
request.send(requestData);

仔细看这只是一个promise的then函数,只有在promise的状态变成成功后才会执行,而刚才我们分析了,cancel就是让这个promise的状态变成成功,所以如果执行了,取消请求的函数,这个then就会执行,取消发送请求,并且把发送请求的promise变成reject,被axiox.get().catch()捕获; 流程已经清楚了,最后再总结一下: 执行cancel是让token的promise变成成功,在真正发送请求之前,验证token.promise的状态是否已经变了,如果变了,就取消请求,就是这样一个简单的思想来进行取消请求的。

相关资料

点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:axios
推荐阅读
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    10071次阅读 661人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • Vue

    盘点Vue2和Vue3的10种组件通信方式

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。

    4688次阅读 347人点赞 发布时间: 2022-08-19 09:40:16 立即查看
  • JS

    几个高级前端常用的API

    推荐4个前端开发中常用的高端API,分别是MutationObserver、IntersectionObserver、getComputedstyle、getBoundingClientRect、requ...

    14766次阅读 968人点赞 发布时间: 2021-11-11 09:39:54 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    13964次阅读 525人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    12755次阅读 1026人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    16943次阅读 1357人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    15465次阅读 1257人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    30567次阅读 2404人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    26529次阅读 2041人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录