axios处理重复请求的方法小结

2024-04-18 0 466
目录
  • 前言
  • 技术实现
    • 1. 生成key
    • 2. 缓存请求
    • 3. 发布订阅者模式实现
    • 4. 完整代码实现

使用到的技术/库:axios、TS、发布订阅者模式。

本文将使用发布订阅者模式来处理重复的axios请求。将实现“一定时间内发送多个相同的请求时,只向服务器发出第一个请求,然后将第一个请求的响应结果分发给各个请求方法”的效果。

前言

首先,我们要先定义何为重复的请求?

  • 接口地址、类型、参数相同,则视为相同的请求;
  • 上一个请求还未返回响应结果时,又发送一个相同的请求,则视为重复请求。

其次,我们将每次请求生成一个唯一的key并缓存起来,用作判断是否已存在相同的请求。

最后,我们需要实现一个发布订阅者模式,在存在重复请求时,中断请求,并添加订阅,当之前的请求结果返回时,发布给订阅者。

还有一个问题,如何中断请求?只需在axios的请求拦截器中返回一个Promise.reject()即可,这样将会直接执行axios的响应拦截器中的错误处理方法,而不会向服务器发送请求。

技术实现

1. 生成key

根据接口的地址、类型、参数,我们可以判断是否为相同的请求,那我们可以在key中包含这些关键信息。

import { type AxiosRequestConfig } from \’axios\’

const getDataType = (obj: unknown) => {
let res = Object.prototype.toString.call(obj).split(\’ \’)[1]
res = res.substring(0, res.length – 1).toLowerCase()
return res
}

const getKey = (config: AxiosRequestConfig) => {
const { method, url, data, params } = config;
let key = `${method}-${url}`;

try {
if (data && getDataType(data) === \’object\’) {
key += `-${JSON.stringify(data)}`;
} else if (getDataType(data) === \’formdata\’) {
for (const [k, v] of data.entries()) {
if (v instanceof Blob) {
continue;
}

key += `-${k}-${v}`;
}
}

if (params && getDataType(params) === \’object\’) {
key += `-${JSON.stringify(params)}`;
}
} catch (e) {console.error(e);}

return key;
};

判断参数类型是为了处理FormData、二进制等格式情况。

2. 缓存请求

我们可以创建一个全局变量来保存请求信息,在请求拦截器(发送请求之前)中将请求信息添加至全局变量,然后在响应拦截器(请求完成之后)中移除相关信息。

import axios from \’axios\’;

const historyRequests = new Map<string, number>()

axios.interceptors.request.use(
(config) => {
// 生成key
const key = createKey(config);
// 响应拦截器中需要用到
config.headers.key = key;
// 缓存请求信息
historyRequests.set(key, 1);

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

instance.interceptors.response.use(
(res) => {
// 请求完成,删除缓存信息
const key = res.config.headers.key as string;
if (historyRequests.has(key)) {
historyRequests.delete(key);
}

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

3. 发布订阅者模式实现

订阅者通过注册事件到调度中心,当发布者触发事件时,由调度中心执行相应的订阅事件。

export default class EventBus<T extends Record<string | symbol, any>> {
private listeners: Record<keyof T, ((…args: any[]) => void)[]> = {} as any

$on<K extends keyof T>(event: K, callback: T[K]) {
if (!this.listeners[event]) {
this.listeners[event] = []
}
this.listeners[event].push(callback)
}

$emit<K extends keyof T>(event: K, …args: Parameters<T[K]>) {
const callbacks = this.listeners[event]
if (!callbacks || callbacks?.length === 0) {
return
}

callbacks.forEach((callback) => {
callback(…args)
})
}

$off<K extends keyof T>(event: K, listener?: T[K]) {
if (!listener) {
delete this.listeners[event]
return
}

const fns = this.listeners[event]
if (!fns || !fns.length) {
return
}

const idx = fns.indexOf(listener)
if (idx !== -1) {
fns.splice(idx, 1)
}
}

clear() {
this.listeners = {} as any
}
}

4. 完整代码实现

import axios, {
type AxiosRequestConfig,
AxiosError,
type AxiosResponse
} from \’axios\’;
import { getDataType } from \’./utils\’;
import { EventBus } from \’./event/eventBus\’;

interface IBaseResponse {
code: number;
msg: string;
data?: unknown;
}

const createKey = (config: AxiosRequestConfig) => {
const { method, url, data, params } = config;
let key = `${method}-${url}`;

try {
if (data && getDataType(data) === \’object\’) {
key += `-${JSON.stringify(data)}`;
} else if (getDataType(data) === \’formdata\’) {
for (const [k, v] of data.entries()) {
if (v instanceof Blob) {
continue
}
key += `-${k}-${v}`;
}
}
if (params && getDataType(params) === \’object\’) {
key += `-${JSON.stringify(params)}`;
}
} catch (e) {console.error(e);}
return key;
};

const instance = axios.create({
baseURL: \’\’,
timeout: 5000,
withCredentials: true,
headers: {
\’Content-Type\’: \’application/json;\’
}
});

const historyRequests = new Map<string, number>();
instance.interceptors.request.use(
(config) => {
const key = createKey(config);
// 在响应拦截器中需要用到该key发布/订阅事件
config.headers.key = key;
// 判断是否存在相同请求,存在则中断请求
if (historyRequests.has(key)) {
// 为了后续方便处理中断请求超时
config.headers.requestTime = Date.now();

// 抛出错误并传递相应参数
return Promise.reject(
new AxiosError(\’Redundant request\’, \’ERR_REPEATED\’, config)
);
}
historyRequests.set(key, 1);
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
}
);

const responseInterceptor = (res: AxiosResponse<IBaseResponse | Blob>) => {
const result: [
AxiosResponse<IBaseResponse | Blob> | undefined,
AxiosError | undefined
] = [undefined, undefined];
const data = res.data;
// 可根据你的接口响应数据作处理,这里假设code不为200都为错误
if (data instanceof Blob || data.code === 200) {
result[0] = res;
} else {
result[1] = new AxiosError(data.msg);
}
return result;
};

const eventBus = new EventBus<{
[key: string]: (
data?: AxiosResponse<IBaseResponse | Blob>,
error?: AxiosError
) => void;
}>();

instance.interceptors.response.use(
(res) => {
const [data, error] = responseInterceptor(res);

// 如果存在重复请求,则发布结果,执行订阅事件
const key = res.config.headers.key as string;
if (historyRequests.has(key)) {
historyRequests.delete(key);
eventBus.$emit(key, data, error);
}

return data !== undefined ? data : Promise.reject(error);
},
(error: AxiosError) => {
// 处理中断的重复请求
if (error.code === \’ERR_REPEATED\’) {
return new Promise((resolve, reject) => {
const config = error.config!;
const key = config.headers.key as string;
const callback = (
res?: AxiosResponse<IBaseResponse | Blob>,
err?: AxiosError
) => {
res ? resolve(res) : reject(err);
eventBus.$off(key, callback);
};
// 订阅事件
eventBus.$on(key, callback);

// 处理超时
const timeout = config.timeout || 5000;
const requestTime = config.headers.requestTime as number;
const now = Date.now();
if (now – requestTime > timeout) {
historyRequests.delete(key);
const error = new AxiosError(
`timeout of ${timeout}ms exceeded`,
\’ECONNABORTED\’,
config
);
error.name = \’AxiosError\’;
eventBus.$emit(key, undefined, error);
}
});
}

return Promise.reject(error);
}
);

可以根据你的实际需求调整相关代码,例如:你需要对某些请求放行,则可以通过在headers中添加一个属性来控制是否允许重复请求。

以上就是axios处理重复请求的方法小结的详细内容,更多关于axios处理重复请求的资料请关注悠久资源网其它相关文章!

您可能感兴趣的文章:

  • Vue中Axios中取消请求及阻止重复请求的方法
  • 浅谈axios中取消请求及阻止重复请求的方法
  • 浅谈Axios去除重复请求方案
  • 项目中如何使用axios过滤多次重复请求详解
  • axios取消请求与避免重复请求
  • Axios取消重复请求的方法实例详解

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悠久资源 JavaScript axios处理重复请求的方法小结 https://www.u-9.cn/biancheng/javascript/186911.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务