axios 取消请求

4,568 阅读2分钟

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

当切换步骤或切换tab等情况,前一个组件中发起的请求可能还在进行中,用户就已经退出了步骤流程,也可能是切换到了其他的tab页面,之前的请求还在进行中,如果不去处理未完成的请求,会极大地影响页面性能,甚至导致后续的请求超时,或者数据处理不正确,页面展示错误等等

对于以上类似的问题,我们就需要知道如何取消请求

官方示例

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});

// cancel the request
cancel();

在请求配置之中实例化axios上的CancelToken方法,回调函数的返回值就是取消当前请求的方法:cancel,只需要把cancel保存到一个地方就可以随时调用,用来取消这个请求

在实际项目中我们往往不会像官网例子中那样使用,更多的是在axios的拦截器中做全局配置管理,需要对上面的代码进行一些改变

项目具体实现

项目中使用 Vuex 和 axios 请求和响应拦截器,在 axios 请求拦截器中实例化cancelToken然后把回调的取消请求方法,以请求的函数名为 key ,以取消请求的方法为 val ,注册到 Vuex 中,那么就可以在在这个请求周期内利用Vuex去调用这个方法,用来取消这个请求。

axios 拦截器

Vue 状态管理模式 -- Vuex

Vuex

store 文件夹中相关 modules 配置中增加 cancel 相关 state,actions 等

const state = {
  cancel: {},
}

const mutations = {
  CANCEL(state, { funNames = [], msg = "用户手动取消网络请求" }) {
    if (!Object.keys(state.cancel).length) {
      return false
    }
    for (const key in state.cancel) {
      if (state.cancel.hasOwnProperty(key)) {
        if (funNames.includes(key)) {
          if (!state.cancel[key].response) {
            state.cancel[key].cancel(msg);
            state.cancel[key].response = true;
          }
        }
      } else {
        return false
      }
    }
  },
  SET_CANCEL(state, { cancel, funName }) {
    state.cancel[funName] = { cancel, response: false };
  },
  RESPONSE(state, funName) {
    if (Object.keys(state.cancel).includes(funName)) {
      state.cancel[funName].response = true;
    }
  },
}

const actions = {
  setCancel({ commit }, fn){
    commit('SET_CANCEL', fn)
  },
  response({ commit }, res){
    commit('RESPONSE', res)
  },
  cancel({ commit }, res) {
    commit('CANCEL', res)
  }
}

Axios

拦截器中配置 cancel 相关拦截处理

import axios from "axios";
import store from "@/store";

var instance = axios.create({
  timeout: 30000,
  withCredentials: true
});

const CancelToken = axios.CancelToken;

// 请求拦截
instance.interceptors.request.use(
  req => {
    req.cancelToken = new CancelToken(cancel => {
      let url = req.url
      let str = url.split('/')
      str = Array.from(new Set(str))
      let index = str.length - 1
      let funName = str[index]
      store.dispatch("setCancel", { cancel, funName: funName })
    });
    return req;
  },
  err => Promise.reject(err)
);

// 响应拦截
instance.interceptors.response.use(
  function (response) {
    store.dispatch("response", response.config.funName);
    if (response.status === 200 && response.data.request_id) {
      if (response.data.code == 0) {
        return response.data;
      } else {
        let msg = response.data.msg
        Message.error({
          message: msg,
          duration: 3000,
          center: true,
          offset: 50,
          showClose: true
        });
        return Promise.reject(error);
      }
    }
    return response;
  },

调用

以上配置好后,就可以在需要的业务方法中调用

funNames 设置需要取消的请求API名,就可以同时取消一个或多个发送中的请求

handleCancel() {
    this.$store.dispatch("cancel", { funNames: ["flavors","transerver_info"] })
}

快试试吧~