Technology/library used:axios
、TS
、Publish Subscriber Mode
。
This article will usePublish Subscribers
Mode to handle duplicate axios requests. The effect of "when multiple identical requests are sent within a certain period of time, only the first request is issued to the server, and then the response result of the first request is distributed to each request method".
Preface
First, we need to define what a duplicate request is?
- If the interface address, type, and parameters are the same, it will be considered the same request;
- When the previous request has not returned the response result, another same request is sent, which is considered a duplicate request.
Secondly, we generate a unique key for each request and cache it to determine whether the same request already exists.
Finally, we need to implement aPublish Subscriber Mode
, when there is a duplicate request, the request is interrupted, and the subscription is added, and when the result of the previous request is returned, it is published to the subscriber.
There is another question, how to interrupt the request? Just inaxios
Returns a request interceptor()
That's right, it will be executed directlyaxios
response to error handling methods in interceptor without sending a request to the server.
Technology implementation
1. Generate key
Based on the address, type, and parameters of the interface, we can determine whether it is the same request, and we can include these key information in the key.
import { type AxiosRequestConfig } from 'axios' const getDataType = (obj: unknown) => { let res = (obj).split(' ')[1] res = (0, - 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 += `-${(data)}`; } else if (getDataType(data) === 'formdata') { for (const [k, v] of ()) { if (v instanceof Blob) { continue; } key += `-${k}-${v}`; } } if (params && getDataType(params) === 'object') { key += `-${(params)}`; } } catch (e) {(e);} return key; };
The parameter type is judged for processingFormData
, binary formats.
2. Cache requests
We can create a global variable to hold the request information, add the request information to the global variable in the request interceptor (before sending the request), and then remove the relevant information in the response interceptor (after the request is completed).
import axios from 'axios'; const historyRequests = new Map<string, number>() ( (config) => { // Generate key const key = createKey(config); // It is required in the response interceptor = key; // Cache request information (key, 1); return config; }, (error) => { return (error); } ); ( (res) => { // The request is completed, delete the cache information const key = as string; if ((key)) { (key); } return res; }, (error) => { return (error); } );
3. Publish subscriber mode implementation
Subscribers register events to the dispatch center. When the publisher triggers an event, the dispatch center executes the corresponding subscription event.
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 (![event]) { [event] = [] } [event].push(callback) } $emit<K extends keyof T>(event: K, ...args: Parameters<T[K]>) { const callbacks = [event] if (!callbacks || callbacks?.length === 0) { return } ((callback) => { callback(...args) }) } $off<K extends keyof T>(event: K, listener?: T[K]) { if (!listener) { delete [event] return } const fns = [event] if (!fns || !) { return } const idx = (listener) if (idx !== -1) { (idx, 1) } } clear() { = {} as any } }
4. Complete code implementation
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 += `-${(data)}`; } else if (getDataType(data) === 'formdata') { for (const [k, v] of ()) { if (v instanceof Blob) { continue } key += `-${k}-${v}`; } } if (params && getDataType(params) === 'object') { key += `-${(params)}`; } } catch (e) {(e);} return key; }; const instance = ({ baseURL: '', timeout: 5000, withCredentials: true, headers: { 'Content-Type': 'application/json;' } }); const historyRequests = new Map<string, number>(); ( (config) => { const key = createKey(config); // This key publish/subscribe event is required in the response interceptor = key; // Determine whether the same request exists, and interrupt the request if it exists if ((key)) { // For subsequent convenience, timeout of interrupt request processing = (); // Throw an error and pass the corresponding parameters return ( new AxiosError('Redundant request', 'ERR_REPEATED', config) ); } (key, 1); return config; }, (error: AxiosError) => { return (error); } ); const responseInterceptor = (res: AxiosResponse<IBaseResponse | Blob>) => { const result: [ AxiosResponse<IBaseResponse | Blob> | undefined, AxiosError | undefined ] = [undefined, undefined]; const data = ; // You can process it according to your interface response data. Assuming that the code is not 200 is an error if (data instanceof Blob || === 200) { result[0] = res; } else { result[1] = new AxiosError(); } return result; }; const eventBus = new EventBus<{ [key: string]: ( data?: AxiosResponse<IBaseResponse | Blob>, error?: AxiosError ) => void; }>(); ( (res) => { const [data, error] = responseInterceptor(res); // If there is a duplicate request, publish the result and execute the subscription event const key = as string; if ((key)) { (key); eventBus.$emit(key, data, error); } return data !== undefined ? data : (error); }, (error: AxiosError) => { // Handle repeated requests for interruption if ( === 'ERR_REPEATED') { return new Promise((resolve, reject) => { const config = !; const key = as string; const callback = ( res?: AxiosResponse<IBaseResponse | Blob>, err?: AxiosError ) => { res ? resolve(res) : reject(err); eventBus.$off(key, callback); }; // Subscribe to events eventBus.$on(key, callback); // Processing timeout const timeout = || 5000; const requestTime = as number; const now = (); if (now - requestTime > timeout) { (key); const error = new AxiosError( `timeout of ${timeout}ms exceeded`, 'ECONNABORTED', config ); = 'AxiosError'; eventBus.$emit(key, undefined, error); } }); } return (error); } );
You can adjust the relevant code according to your actual needs, for example: if you need to release certain requests, you can use it inheaders
Add an attribute to control whether duplicate requests are allowed.
The above is the detailed content of the method of axios handling duplicate requests. For more information about axios handling duplicate requests, please pay attention to my other related articles!