Axios 取消请求:如何优雅终止异步操作?

1,818 阅读3分钟

Axios 请求取消功能完整指南

1. Axios 请求取消的基本概念

Axios 提供了两种主要方式来取消正在进行的请求:

  • CancelToken:通过创建一个取消令牌(cancel token),将其附加到请求中,当需要取消请求时调用 cancel() 方法。
  • AbortController:基于 Fetch API 的 AbortController 实现,通过传递 AbortController 实例的 signal 属性来取消请求。

这两种方法各有优缺点,但目前推荐使用 AbortController,因为它是现代浏览器支持的标准方法,而 CancelToken 已被弃用。

2. 使用 CancelToken 取消请求

  1. 创建 CancelToken 对象
// 创建取消令牌源
const CancelToken = axios.CancelToken
const source = CancelToken.source()
  1. 将 CancelToken 附加到请求
axios.get('https://api.example.com/data ', { cancelToken: source.token })
   .then(response => {
       console.log(response.data);
   })
   .catch(error => {
       if (axios.isCancel(error)) {
           console.log('请求被取消:', error.message);
       } else {
           console.error('其他错误:', error);
       }
   });
  1. 取消请求
   cancelToken.cancel('操作已取消');

需要注意的是,一旦取消请求,CancelTokentoken 将失效,需要重新创建新的 CancelToken 对象。

3. 使用 AbortController 取消请求

从 Axios v0.22.0 版本开始,推荐使用 AbortController 来取消请求。以下是具体实现步骤:

  1. 创建 AbortController 实例
const controller = new AbortController();
const signal = controller.signal;
  1. 将信号传递给 Axios 请求
axios.get('https://api.example.com/data ', { signal })
   .then(response => {
       console.log(response.data);
   })
   .catch(error => {
       if (error.name  === 'AbortError') {
           console.log('请求被取消:', error.message);
       } else {
           console.error('其他错误:', error);
       }
   });
  1. 取消请求
controller.abort('操作已取消');

相比 CancelTokenAbortController 更符合现代 Web 标准,并且支持更广泛的浏览器环境。

5.封装在拦截器中

基于 CancelToken

封装请求

import axios from "axios";
​
const service = axios.create({
  baseURL: process.env.VUE_APP_API_BASE_URL,
  timeout: 5000,
});
​
let cancel;
​
const cancelRequest=(val)=> {
  if (cancel) {
    cancel(val || 'cancel request');
    cancel = null; // 重置请求
  }
}
​
service.interceptors.request.use(
  (config) => {
    if (cancel) {
      cancel();
    }
​
    config.cancelToken = new axios.CancelToken(function executor(c) {
      cancel = c;
    });
​
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);
​
service.interceptors.response.use(
  (response) => {
    return response;
  },
  (error) => {
    if (axios.isCancel(error)) {
      console.log("Request canceled:", error.message);
      return Promise.reject(error);
    }
    return Promise.reject(error);
  }
);
//将取消请求的方法暴露
service.cancelRequest=cancelRequest
export default service;
​

触发方式

<template>
  <div class="">
    <button @click="send">发送请求</button>
    <button @click="cancel">取消请求</button>
  </div>
</template><script>
import http from "@/utils/request"; 
export default {
  data() {
    return {
​
    };
  },
  methods: {
    send() {
      http
        .get("/xxx")
        .then((response) => console.log(response.data))
        .catch((error) => console.error(error));
    },
    cancel() {
      http.cancelRequest("主动取消了请求");
    },
  },
};
</script>

基于 AbortController

import axios from 'axios';

const service = axios.create({
    baseURL: process.env.VUE_APP_API_BASE_URL,
    timeout: 5000,
});

let controller;


const abortRequest=(val) =>{
    
    if (controller) {
        controller.abort(val || 'Request canceled ');
        controller = null; 
     }
}

service.interceptors.request.use((config) => {
     if (controller) { 
         abort(); 
     } 

     controller = new AbortController(); 
     config.signal = controller.signal; 

     return config; 
}, (error) => { 
     return Promise.reject(error); 
}); 

service.interceptors.response.use((response) => { 
     return response; 
}, (error) => { 
     if (error.name === 'AbortError') { 
         console.log('Request canceled:', error.message); 
         return Promise.reject(error); 
     } 
     return Promise.reject(error); 
}); 
service.abortRequest = abortRequest;

export default service; 

触发方式

<template>
  <div class="">
    <button @click="send">发送请求</button>
    <button @click="cancel">取消请求</button>
  </div>
</template>

<script>
import http from "@/utils/request"; 
export default {
  data() {
    return {

    };
  },
  methods: {
    send() {
      http
        .get("/xxx")
        .then((response) => console.log(response.data))
        .catch((error) => console.error(error));
    },
    cancel() {
      http.abortRequest("主动取消了请求");
    },
  },
};
</script>

6. Axios 请求取消的注意事项

  • 取消时机:只有在请求尚未发送时才能取消(也就是pending时候)。如果请求已经发送,则无法取消。
  • 错误处理:当请求被取消时,Axios 会抛出一个错误。可以通过捕获错误并检查其类型来判断是否为取消操作。
  • 资源管理:取消请求后,应确保释放相关资源,例如清除存储的取消函数或关闭控制器实例。

7. 实际应用场景

  • 用户导航离开页面:当用户点击导航按钮时,可以调用取消函数以停止正在进行的请求。
  • 重复请求管理:在短时间内连续发送相同请求时,可以通过取消旧请求来避免重复响应。(通过 URL和参数生成唯一标识符(如 ${method}-${url}),用于判断是否为相同请求。)
  • 动态搜索功能:在用户输入内容变化时,可以取消正在进行的 API 请求以减少资源消耗。

8. 总结

Axios 提供了灵活的请求取消机制,通过 CancelTokenAbortController 可以有效管理异步请求。尽管 CancelToken 已被弃用,但 AbortController 是目前推荐的最佳实践。开发者可以根据项目需求选择合适的方法,并注意正确处理取消后的资源释放和错误捕获。