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企服之家,中国第一个企服评测及商务社交产业平台。 |