Axios使用cancel token取消请求后,无法再次发送请求的问题及解决思路

5,672 阅读3分钟

前言

在一个vue项目开发的过程中,遇到一个需要中断文件上传的需求,当我利用axios的cancel token实现中断请求的功能之后,想要再次发送post请求,却发现axios直接返回了reject。

axios中关于cancel token的文档

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.');


// axios实现取消请求的步骤:
// 1.利用axios.CancelToken.source()方法生成一个source对象
// (source对象下有一个cancel方法和一个token对象)
// 2.在请求的config中,将source.token赋值给config的cancelToken属性
// 3.发送请求之后,若要取消请求,则执行sourced对象的cancel方法

我的实现方案

getData.js 统一封装的请求接口

// fileObj: 文件对象
// source: axios.CancelToken.source()方法生成的source对象

upload(fileObj, source) {
  const formData = new FormData()
  formData.append('file', fileObj)
  return server.post(url, formData, {
      timeout: 9999999,  //防止超时
      cancelToken: source.token  //用surce.token标记请求
  })
}
--------------------------------------------------------
upload.vue 上传文件的组件

import axios from 'axios'
export default {
  data() {
    const source = axios.CancelToken.source()
    return{
      source,   // 将source保存在data中
      fileObj: ''  //文件对象
    }
  },
  methods:{
    // 上传文件
    async upload(){
      let res = null
      try {
        res = await this.$api.upload(this.fileObj, this.source)
      } catch (e) {
        console.log(e.message)
      }
    },
    // 中断上传
    cancelReauest() {
      this.source.cancel('stop')
    }
  }
}

问题复现

  • 当我执行upload方法时,文件能够正常上传
  • 点击中断按钮,执行cancelReauest方法,文件上传中断,控制台打印错误信息stop
  • 再次上传文件,执行upload方法时,请求却直接中断,错误被catch捕获,控制台打印错误信息stop

按照我的理解,此时我并没有去执行cancelReauest方法,文件应该是正常上传才对,但此时请求却直接中断,不太能理解,搜索完谷歌和百度之后也没有类似的问题和解决方案,所以只能自己摸索

猜测及理解

在执行cancelReauest方法之前,我打印了source对象,查看了它的数据结构,发现他token下有一个promise对象,此时它的状态是pending

image.png

当我执行完cancelReauest方法之后,此时promise的状态变成了fulfilled,并且多了一个reason对象,里面放着我上次停止文件上传传递的信息stop

image.png

于是我大胆猜测,文件上传的中断,是根据source.tokenpromise的状态进行控制的

  • 当状态为pending时,可以正常上传文件
  • 当状态为fulfilled时,文件上传中断

而在我的代码中,文件上传中断之后

  • 存储在data中的source对象,它token里的promise状态已经修改为fulfilled
  • 当我再次发送请求,config中携带的是状态为fulfilledsource对象,所有请求直接中断

为了验证我的猜想,我在文件上传中断之后,重新给source对象赋值,刷新他的promise状态

cancelReauest() {
  this.source.cancel('stop')
  this.source = axios.CancelToken.source()   //重新赋值
}

修改完毕之后,当我再次发送请求,成功上传!

问题解决,完结撒花!