渣渣兔 发表于 2025-3-19 12:56:48

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

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 实例的创建

const instance: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
headers: {
    'Content-Type': 'application/json',
},
});


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

const cancelTokenMap = new Map<string, CancelTokenSource>();


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

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);
}
);


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

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);
}
);


[*]功能:

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

[*]作用: 实现全局的响应和错误处置惩罚逻辑。
1.5 封装的请求方法

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 });
},
// 取消指定请求
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);
    });
},
};


[*]功能:

[*]封装了 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 方法取消之前的请求,只保存最后一次请求。
http.get('/api/data')
.then((data) => console.log(data))
.catch((error) => {
    if (axios.isCancel(error)) {
      console.log('Request canceled:', error.message);
    } else {
      console.error('Error:', error);
    }
});

// 取消之前的请求
http.cancelRequest('/api/data');
3.2 页面跳转时取消请求

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

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, CancelTokenSource } from 'axios';// 创建axios实例const instance: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
headers: {
    'Content-Type': 'application/json',
},
});
// 创建一个Map来存储取消令牌const cancelTokenMap = new Map<string, CancelTokenSource>();
// 请求拦截器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企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Axios 请求取消:从原理到实践