SoFunction
Updated on 2025-04-10

Summary of the method of axios handling duplicate requests

Technology/library used:axiosTSPublish Subscriber Mode

This article will usePublish SubscribersMode 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 inaxiosReturns a request interceptor()That's right, it will be executed directlyaxiosresponse 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) =&gt; {
  const { method, url, data, params } = config;
  let key = `${method}-${url}`;

  try {
    if (data &amp;&amp; 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 &amp;&amp; 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&lt;string, number&gt;();
(
  (config) =&gt; {
    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) =&gt; {
    return (error);
  }
);

const responseInterceptor = (res: AxiosResponse&lt;IBaseResponse | Blob&gt;) =&gt; {
  const result: [
    AxiosResponse&lt;IBaseResponse | Blob&gt; | 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&lt;{
  [key: string]: (
    data?: AxiosResponse&lt;IBaseResponse | Blob&gt;,
    error?: AxiosError
  ) =&gt; void;
}&gt;();

(
  (res) =&gt; {
    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) =&gt; {
    // Handle repeated requests for interruption    if ( === 'ERR_REPEATED') {
      return new Promise((resolve, reject) =&gt; {
        const config = !;
        const key =  as string;
        const callback = (
          res?: AxiosResponse&lt;IBaseResponse | Blob&gt;,
          err?: AxiosError
        ) =&gt; {
          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 &gt; 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 inheadersAdd 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!