axios使用 CancelToken / AbortController 方法进行取消请求

文章目录

  • [1. CancelToken 方法使用示例](#1. CancelToken 方法使用示例)
    • [1.1 请求拦截 / 响应器写法](#1.1 请求拦截 / 响应器写法)
    • [1.2 接口定义写法](#1.2 接口定义写法)
    • [1.3 具体使用写法](#1.3 具体使用写法)
  • [2. AbortController 方法使用示例(axios~v0.22.0 及以后版本)](#2. AbortController 方法使用示例(axios~v0.22.0 及以后版本))
    • [2.1 请求拦截 / 响应器写法](#2.1 请求拦截 / 响应器写法)
    • [2.2 接口定义写法](#2.2 接口定义写法)
    • [2.3 具体使用写法](#2.3 具体使用写法)

1. CancelToken 方法使用示例

1.1 请求拦截 / 响应器写法

javascript 复制代码
// requer.js 文件
import router from '@/router'
import { ElMessage } from 'element-plus'

// 全版本兼容 CancelToken 方法 -- 示例
import axios, { CancelToken } from 'axios';
// import { getToken, setToken, getRootDomain } from './auth'

// 全局请求控制器
export const requestController = {
    pendingRequests: new Map(),

    addRequest (key, cancel) {
        this.pendingRequests.set(key, cancel);
    },

    cancelRequest (key, message = '请求已取消') {
        if (this.pendingRequests.has(key)) {
            const cancel = this.pendingRequests.get(key);
            cancel(message);
            this.pendingRequests.delete(key);
        }
    },

    removeRequest (key) {
        this.pendingRequests.delete(key);
    }
};

const service = axios.create({
    url: '/',
})

service.interceptors.request.use(config => {

        const requestKey = `${config.method}-${config.url}-${JSON.stringify(config.data || config.params)}`;

        // 取消重复请求
        requestController.cancelRequest(requestKey);

        config.cancelToken = new CancelToken((cancel) => {
            requestController.addRequest(requestKey, cancel);
        });

        return config;
    },

    (error) => {
        // if(store.loading)
        // Do something with request error
        // console.log(error); // for debug
        Promise.reject(error);
    },
);



service.interceptors.response.use(
    res => {
        const { data } = res

        const requestKey = `${res.config.method}-${res.config.url}-${JSON.stringify(res.config.data || res.config.params)}`;
        requestController.removeRequest(requestKey);

        if (data.code == 401) {
            window.sessionStorage.clear()
            router.replace('/login')
            return Promise.reject(data)
        }

        if (data.code == 500) {
            ElMessage({
                type: 'error',
                message: data?.message || '网络错误',
                center: true,
            })
            return Promise.reject(data)
        }

        return data
    },
    (error) => {
        if (axios.isCancel(error)) {
            console.log('请求被取消:', error.message);
            return new Promise(() => { });
        }
        // console.log('err' + error); // for debug
        ElMessage({
            message:
                (error && error.message) ||
                '网络错误或获取失败,请刷新后重试', // error.message,
            type: 'error',
            duration: 5 * 1000,
        });
        return Promise.reject(error);
    },
)


export default service

1.2 接口定义写法

javascript 复制代码
// api/index.js 文件
import service, { requestController } from '@/utils/requer'

// 不需要主动取消请求写法
// export const userCopyAdd = (data) => {
//   return service({
//     url: '/api/user-copy/add',
//     method: 'POST',
//     data
//   })
// }

// 需要主动取消请求写法
export const userCopyAdd = (data) => {
  const request = {
    service: () => {
      return service({
        url: '/api/user-copy/add',
        method: 'POST',
        data,
      });
    },
    cancel: () => {
      // 这里有个注意点,请求方式必须是小写,不论请求本身是怎么写的
      const key = `post-/api/user-copy/add-${JSON.stringify(data)}`;
      requestController.cancelRequest(key);
    }
  };

  return request;
}

1.3 具体使用写法

javascript 复制代码
<template>
  <!-- 取消重复请求 -- 由于接口响应速度比较快,需要将网络调整成3G网或者快速点击 -->
  <!-- 请求的接口:/user-copy/add 需启动 nest-moogodb 项目,项目地址:https://gitee.com/kuxiao-smile/node-nest -->
  <el-button size="small" @click="setUserCopyAdd">重复请求测试</el-button>
  <el-button size="small" @click="cancelUserCopyAdd">取消请求测试</el-button>
</template>
<script setup>
import { ref } from 'vue';
import { userCopyAdd } from '@/api/index'

const ruleForm = ref({})

// 添加重复请求接口拦截
const setUserCopyAdd = () => {
  // userCopyAdd(ruleForm.value)
  userCopyAdd(ruleForm.value).service().then(res => {
    ElMessage.success('添加成功')
  }).catch(error => {
    console.error('添加失败:', error); // 添加错误捕获
    ElMessage.error('添加失败: ' + (error.message || '未知错误'));
  })
}

// 取消请求
const cancelUserCopyAdd = () => {
  userCopyAdd(ruleForm.value).cancel()
}
</script>

<style lang="less" scoped></style>

2. AbortController 方法使用示例(axios~v0.22.0 及以后版本)

2.1 请求拦截 / 响应器写法

javascript 复制代码
// requer.js 文件
import router from '@/router'
import { ElMessage } from 'element-plus'

// v0.22.0 及以后版本 ( 以前版本此方法无效 ) AbortController 方法 -- 使用示例

import axios from 'axios';

export const requestController = {
  pendingRequests: new Map(),

  addRequest(key, controller) {
    this.pendingRequests.set(key, controller);
  },

  cancelRequest(key, message = '请求已取消') {
    if (this.pendingRequests.has(key)) {
      const controller = this.pendingRequests.get(key);
      controller.abort(message);
      this.pendingRequests.delete(key);
    }
  },

  removeRequest(key) {
    this.pendingRequests.delete(key);
  }
};

// 创建axios实例
const service = axios.create({
  baseURL: '/',
});

// request拦截器
service.interceptors.request.use(
  (config) => {
    const requestKey = `${config.method}-${config.url}-${JSON.stringify(config.data || config.params)}`;

    // 取消重复请求
    requestController.cancelRequest(requestKey);

    // 使用AbortController
    const controller = new AbortController();
    config.signal = controller.signal;
    requestController.addRequest(requestKey, controller);

    return config;
  },

  (error) => {
    // if(store.loading)
    // Do something with request error
    // console.log(error); // for debug
    Promise.reject(error);
  },
);

service.interceptors.response.use(
    res => {
        const { data } = res

        const requestKey = `${res.config.method}-${res.config.url}-${JSON.stringify(res.config.data || res.config.params)}`;
        requestController.removeRequest(requestKey);

        if (data.code == 401) {
            window.sessionStorage.clear()
            router.replace('/login')
            return Promise.reject(data)
        }

        if (data.code == 500) {
            ElMessage({
                type: 'error',
                message: data?.message || '网络错误',
                center: true,
            })
            return Promise.reject(data)
        }

        return data
    },
    (error) => {
        if (axios.isCancel(error)) {
            console.log('请求被取消:', error.message);
            return new Promise(() => { });
        }
        // console.log('err' + error); // for debug
        ElMessage({
            message:
                (error && error.message) ||
                '网络错误或获取失败,请刷新后重试', // error.message,
            type: 'error',
            duration: 5 * 1000,
        });
        return Promise.reject(error);
    },
)


export default service

2.2 接口定义写法

javascript 复制代码
// api/index.js 文件
import service, { requestController } from '@/utils/requer'

// 不需要主动取消请求写法
// export const userCopyAdd = (data) => {
//   return service({
//     url: '/api/user-copy/add',
//     method: 'POST',
//     data
//   })
// }

// 需要主动取消请求写法
export const userCopyAdd = (data) => {
  const request = {
    service: () => {
      return service({
        url: '/api/user-copy/add',
        method: 'POST',
        data,
      });
    },
    cancel: () => {
      // 这里有个注意点,请求方式必须是小写,不论请求本身是怎么写的
      const key = `post-/api/user-copy/add-${JSON.stringify(data)}`;
      requestController.cancelRequest(key);
    }
  };

  return request;
}

2.3 具体使用写法

javascript 复制代码
<template>
  <!-- 取消重复请求 -- 由于接口响应速度比较快,需要将网络调整成3G网或者快速点击 -->
  <!-- 请求的接口:/user-copy/add 需启动 nest-moogodb 项目,项目地址:https://gitee.com/kuxiao-smile/node-nest -->
  <el-button size="small" @click="setUserCopyAdd">重复请求测试</el-button>
  <el-button size="small" @click="cancelUserCopyAdd">取消请求测试</el-button>
</template>
<script setup>
import { ref } from 'vue';
import { userCopyAdd } from '@/api/index'

const ruleForm = ref({})

// 添加重复请求接口拦截
const setUserCopyAdd = () => {
  // userCopyAdd(ruleForm.value)
  userCopyAdd(ruleForm.value).service().then(res => {
    ElMessage.success('添加成功')
  }).catch(error => {
    console.error('添加失败:', error); // 添加错误捕获
    ElMessage.error('添加失败: ' + (error.message || '未知错误'));
  })
}

// 取消请求
const cancelUserCopyAdd = () => {
  userCopyAdd(ruleForm.value).cancel()
}
</script>

<style lang="less" scoped></style>
相关推荐
子春一7 小时前
Flutter for OpenHarmony:音律尺 - 基于Flutter的Web友好型节拍器开发与节奏可视化实现
前端·flutter
JarvanMo7 小时前
150万开发者“被偷家”!这两款浓眉大眼的 VS Code 插件竟然是间谍
前端
亿元程序员7 小时前
大佬,现在AI游戏开发教程那么多,你不搞点卖给大学生吗?
前端
未来龙皇小蓝8 小时前
RBAC前端架构-02:集成Vue Router、Vuex和Axios实现基本认证实现
前端·vue.js·架构
空白诗8 小时前
高级进阶 React Native 鸿蒙跨平台开发:slider 滑块组件 - 进度条与评分系统
javascript·react native·react.js
晓得迷路了8 小时前
栗子前端技术周刊第 116 期 - 2025 JS 状态调查结果、Babel 7.29.0、Vue Router 5...
前端·javascript·vue.js
How_doyou_do8 小时前
执行上下文、作用域、闭包 patch
javascript
叫我一声阿雷吧8 小时前
深入理解JavaScript作用域和闭包,解决变量访问问题
开发语言·javascript·ecmascript
顾北128 小时前
AI对话应用接口开发全解析:同步接口+SSE流式+智能体+前端对接
前端·人工智能
iDao技术魔方8 小时前
深入Vue 3响应式系统:为什么嵌套对象修改后界面不更新?
javascript·vue.js·ecmascript