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>
相关推荐
LYFlied2 小时前
Webpack详细打包流程解析
前端·面试·webpack·node.js·打包·工程化
明朝百晓生2 小时前
强化学习[page14]【chapter7】Temporal-Difference Learning (TD learning)
前端·html
我只会写Bug啊2 小时前
B站/爱奇艺防录屏防截屏原理及Vue3实战实现
前端·软件开发
蜗牛攻城狮2 小时前
前端构建工具详解:Vite 与 Webpack 深度对比与实战指南
前端·webpack·vite·构建工具
北极象2 小时前
Electron 通用技术架构分析
javascript·架构·electron
吃好喝好玩好睡好2 小时前
基于 Electron+Flutter 的跨平台桌面端实时屏幕标注与录屏工具深度实践
javascript·flutter·electron
IT_陈寒2 小时前
Redis 性能翻倍的 5 个冷门技巧,90%开发者都不知道的底层优化!
前端·人工智能·后端
亿牛云爬虫专家2 小时前
当数据开始“感知页面”
javascript·html·爬虫代理·代理ip·playwright·页面渲染·dom结构