Axios 请求取消:从原理到实践

打印 上一主题 下一主题

主题 942|帖子 942|积分 2826

Axios 请求取消:从原理到实践

在现代前端开辟中,网络请求是不可或缺的一部门。Axios 是一个基于 Promise 的 HTTP 客户端,广泛应用于浏览器和 Node.js 环境中。然而,在某些场景下,我们可能需要取消正在进行的请求,比方用户在请求完成前跳转到其他页面,大概重复触发雷同的请求时取消之前的请求。本文将深入探讨 Axios 请求取消的原理,并通过一个二次封装的例子来演示怎样实现请求取消。
1. 请求取消的原理

Axios 的请求取消功能依赖于 CancelToken。CancelToken 是一个用于取消请求的令牌,它可以通过 CancelToken.source() 方法创建。每个 CancelToken 实例都有一个 token 和一个 cancel 方法。当调用 cancel 方法时,与该 token 关联的请求将被取消。
1.1 CancelToken 的工作原理


  • 创建 CancelToken: 通过 CancelToken.source() 方法创建一个 CancelToken 实例,该实例包含一个 token 和一个 cancel 方法。
  • 关联请求: 在发起请求时,将 token 通报给 Axios 请求设置中的 cancelToken 字段。
  • 取消请求: 当需要取消请求时,调用 cancel 方法,Axios 会中断与该 token 关联的请求。
1.2 取消请求的流程


  • 用户触发某个操作,发起一个请求。
  • 在请求完成之前,用户触发了另一个操作,需要取消之前的请求。
  • 调用 cancel 方法,Axios 中断之前的请求。
  • 发起新的请求。
2. 二次封装 Axios 实现请求取消

为了更好地管理请求取消逻辑,我们可以对 Axios 进行二次封装。以下是一个简单的封装示例,展示了怎样在封装中实现请求取消功能。

1. 代码结构分析

1.1 Axios 实例的创建

  1. const instance: AxiosInstance = axios.create({
  2.   baseURL: import.meta.env.VITE_API_BASE_URL,
  3.   timeout: 10000,
  4.   headers: {
  5.     'Content-Type': 'application/json',
  6.   },
  7. });
复制代码


  • baseURL: 从环境变量中获取 API 的基础 URL。
  • timeout: 设置请求超时时间为 10 秒。
  • headers: 设置默认请求头为 application/json。
通过 axios.create 创建了一个 Axios 实例 instance,后续的全部请求都将基于这个实例。

1.2 取消令牌的管理

  1. const cancelTokenMap = new Map<string, CancelTokenSource>();
复制代码


  • cancelTokenMap: 使用 Map 数据结构来存储每个请求的取消令牌。键是请求的 URL,值是对应的 CancelTokenSource。
  • 作用: 通过 URL 快速查找和取消对应的请求。

1.3 请求拦截器

  1. instance.interceptors.request.use(
  2.   (config) => {
  3.     // 添加 token 到请求头
  4.     const token = localStorage.getItem('token');
  5.     if (token) {
  6.       config.headers = config.headers || {};
  7.       config.headers.Authorization = `Bearer ${token}`;
  8.     }
  9.     return config;
  10.   },
  11.   (error) => {
  12.     return Promise.reject(error);
  13.   }
  14. );
复制代码


  • 功能: 在请求发送前,查抄本地存储中是否存在 token,如果存在则将其添加到请求头中。
  • 作用: 实现全局的请求头管理,比方身份验证。

1.4 响应拦截器

  1. instance.interceptors.response.use(
  2.   (response: AxiosResponse) => {
  3.     // 清理已完成的请求记录
  4.     const url = response.config.url;
  5.     if (url && cancelTokenMap.has(url)) {
  6.       cancelTokenMap.delete(url);
  7.     }
  8.     // 处理全局响应逻辑
  9.     if (response.data.code !== 0) {
  10.       return Promise.reject(response.data);
  11.     }
  12.     return response.data;
  13.   },
  14.   (error) => {
  15.     // 清理失败的请求记录
  16.     const url = error.config?.url;
  17.     if (url && cancelTokenMap.has(url)) {
  18.       cancelTokenMap.delete(url);
  19.     }
  20.     // 处理全局错误
  21.     if (error.response?.status === 401) {
  22.       // 处理未授权
  23.       window.location.href = '/login';
  24.     }
  25.     return Promise.reject(error);
  26.   }
  27. );
复制代码


  • 功能:

    • 在请求乐成时,清理 cancelTokenMap 中对应的请求记载。
    • 在请求失败时,清理 cancelTokenMap 中对应的请求记载,并根据状态码处置惩罚全局错误(比方未授权时跳转到登录页)。

  • 作用: 实现全局的响应和错误处置惩罚逻辑。

1.5 封装的请求方法

  1. const http = {
  2.   get: <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {
  3.     const source = axios.CancelToken.source();
  4.     cancelTokenMap.set(url, source);
  5.     return instance.get(url, { ...config, cancelToken: source.token });
  6.   },
  7.   post: <T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => {
  8.     const source = axios.CancelToken.source();
  9.     cancelTokenMap.set(url, source);
  10.     return instance.post(url, data, { ...config, cancelToken: source.token });
  11.   },
  12.   put: <T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => {
  13.     const source = axios.CancelToken.source();
  14.     cancelTokenMap.set(url, source);
  15.     return instance.put(url, data, { ...config, cancelToken: source.token });
  16.   },
  17.   delete: <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {
  18.     const source = axios.CancelToken.source();
  19.     cancelTokenMap.set(url, source);
  20.     return instance.delete(url, { ...config, cancelToken: source.token });
  21.   },
  22.   // 取消指定请求
  23.   cancelRequest: (url: string) => {
  24.     const source = cancelTokenMap.get(url);
  25.     if (source) {
  26.       source.cancel(`Request canceled: ${url}`);
  27.       cancelTokenMap.delete(url);
  28.     }
  29.   },
  30.   // 取消所有请求
  31.   cancelAllRequests: () => {
  32.     cancelTokenMap.forEach((source, url) => {
  33.       source.cancel(`Request canceled: ${url}`);
  34.       cancelTokenMap.delete(url);
  35.     });
  36.   },
  37. };
复制代码


  • 功能:

    • 封装了 get、post、put、delete 方法,每个方法都会为请求创建一个 CancelToken,并将其存储到 cancelTokenMap 中。
    • 提供了 cancelRequest 和 cancelAllRequests 方法,用于取消指定请求或全部请求。

  • 作用: 简化请求调用,并提供机动的请求取消功能。

2. 请求取消的实现原理

2.1 CancelToken 的作用



  • CancelToken.source(): 创建一个 CancelTokenSource 对象,包含 token 和 cancel 方法。
  • token: 用于关联请求。
  • cancel: 用于取消请求。
2.2 请求取消的流程


  • 发起请求时,创建一个 CancelTokenSource,并将其存储到 cancelTokenMap 中。
  • 如果需要取消请求,调用 cancelRequest 或 cancelAllRequests 方法。
  • 调用 cancel 方法后,Axios 会中断与该 token 关联的请求,并抛出一个 Cancel 错误。
  • 在响应拦截器中,清理已完成的请求记载。

3. 使用场景

3.1 取消重复请求

当用户快速点击按钮多次触发雷同的请求时,可以通过 cancelRequest 方法取消之前的请求,只保存最后一次请求。
  1. http.get('/api/data')
  2.   .then((data) => console.log(data))
  3.   .catch((error) => {
  4.     if (axios.isCancel(error)) {
  5.       console.log('Request canceled:', error.message);
  6.     } else {
  7.       console.error('Error:', error);
  8.     }
  9.   });
  10. // 取消之前的请求
  11. http.cancelRequest('/api/data');
复制代码
3.2 页面跳转时取消请求

当用户跳转到其他页面时,可以通过 cancelAllRequests 方法取消全部未完成的请求,制止无效请求占用资源。
  1. window.addEventListener('beforeunload', () => {
  2.   http.cancelAllRequests();
  3. });
复制代码

完备代码

  1. import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, CancelTokenSource } from 'axios';// 创建axios实例const instance: AxiosInstance = axios.create({
  2.   baseURL: import.meta.env.VITE_API_BASE_URL,
  3.   timeout: 10000,
  4.   headers: {
  5.     'Content-Type': 'application/json',
  6.   },
  7. });
  8. // 创建一个Map来存储取消令牌const cancelTokenMap = new Map<string, CancelTokenSource>();
  9. // 请求拦截器instance.interceptors.request.use(  (config) => {    // 在这里可以添加token等全局请求头    const token = localStorage.getItem('token');    if (token) {      config.headers = config.headers || {};      config.headers.Authorization = `Bearer ${token}`;    }    return config;  },  (error) => {    return Promise.reject(error);  });// 响应拦截器instance.interceptors.response.use(  (response: AxiosResponse) => {    // 清理已完成的请求记载    const url = response.config.url;    if (url && cancelTokenMap.has(url)) {      cancelTokenMap.delete(url);    }    // 在这里处置惩罚全局响应逻辑    if (response.data.code !== 0) {      return Promise.reject(response.data);    }    return response.data;  },  (error) => {    // 清理失败的请求记载    const url = error.config?.url;    if (url && cancelTokenMap.has(url)) {      cancelTokenMap.delete(url);    }    // 在这里处置惩罚全局错误    if (error.response?.status === 401) {      // 处置惩罚未授权      window.location.href = '/login';    }    return Promise.reject(error);  });// 封装请求方法const http = {  get: <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {    const source = axios.CancelToken.source();    cancelTokenMap.set(url, source);    return instance.get(url, { ...config, cancelToken: source.token });  },  post: <T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => {    const source = axios.CancelToken.source();    cancelTokenMap.set(url, source);    return instance.post(url, data, { ...config, cancelToken: source.token });  },  put: <T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => {    const source = axios.CancelToken.source();    cancelTokenMap.set(url, source);    return instance.put(url, data, { ...config, cancelToken: source.token });  },  delete: <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {    const source = axios.CancelToken.source();    cancelTokenMap.set(url, source);    return instance.delete(url, { ...config, cancelToken: source.token });  },  // 添加取消请求的方法  // http.cancelRequest('/api/some-endpoint');  cancelRequest: (url: string) => {    const source = cancelTokenMap.get(url);    if (source) {      source.cancel(`Request canceled: ${url}`);      cancelTokenMap.delete(url);    }  },  // 取消全部请求  cancelAllRequests: () => {    cancelTokenMap.forEach((source, url) => {      source.cancel(`Request canceled: ${url}`);      cancelTokenMap.delete(url);    });  },};export default http;
复制代码
4. 总结

通过二次封装 Axios,我们实现了一个功能强盛且易于使用的 HTTP 客户端。它不光支持全局的请求和响应拦截,还提供了机动的请求取消功能,适用于多种场景。盼望本文能帮助你更好地理解和使用 Axios 的请求取消功能。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

渣渣兔

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表