亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

axios處理重復請求的方法小結

 更新時間:2024年03月28日 11:25:46   作者:求知若饑  
這篇文章主要為大家詳細介紹了如何使用發(fā)布訂閱者模式來處理重復的axios請求,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

使用到的技術/庫:axios、TS發(fā)布訂閱者模式。

本文將使用發(fā)布訂閱者模式來處理重復的axios請求。將實現“一定時間內發(fā)送多個相同的請求時,只向服務器發(fā)出第一個請求,然后將第一個請求的響應結果分發(fā)給各個請求方法”的效果。

前言

首先,我們要先定義何為重復的請求?

  • 接口地址、類型、參數相同,則視為相同的請求;
  • 上一個請求還未返回響應結果時,又發(fā)送一個相同的請求,則視為重復請求。

其次,我們將每次請求生成一個唯一的key并緩存起來,用作判斷是否已存在相同的請求。

最后,我們需要實現一個發(fā)布訂閱者模式,在存在重復請求時,中斷請求,并添加訂閱,當之前的請求結果返回時,發(fā)布給訂閱者。

還有一個問題,如何中斷請求?只需在axios的請求攔截器中返回一個Promise.reject()即可,這樣將會直接執(zhí)行axios的響應攔截器中的錯誤處理方法,而不會向服務器發(fā)送請求。

技術實現

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. 緩存請求

我們可以創(chuàng)建一個全局變量來保存請求信息,在請求攔截器(發(fā)送請求之前)中將請求信息添加至全局變量,然后在響應攔截器(請求完成之后)中移除相關信息。

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. 發(fā)布訂閱者模式實現

訂閱者通過注冊事件到調度中心,當發(fā)布者觸發(fā)事件時,由調度中心執(zhí)行相應的訂閱事件。

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發(fā)布/訂閱事件
    config.headers.key = key;
    // 判斷是否存在相同請求,存在則中斷請求
    if (historyRequests.has(key)) {
      // 為了后續(xù)方便處理中斷請求超時
      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);

    // 如果存在重復請求,則發(fā)布結果,執(zhí)行訂閱事件
    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處理重復請求的資料請關注腳本之家其它相關文章!

相關文章

  • JavaScript實現判斷圖片是否加載完成的3種方法整理

    JavaScript實現判斷圖片是否加載完成的3種方法整理

    這篇文章主要介紹了JavaScript實現判斷圖片是否加載完成的3種方法整理,本文講解了onload方法、javascipt原生方法、jquery方法三種方法,需要的朋友可以參考下
    2015-03-03
  • 微信小程序實現的貪吃蛇游戲【附源碼下載】

    微信小程序實現的貪吃蛇游戲【附源碼下載】

    這篇文章主要介紹了微信小程序實現的貪吃蛇游戲,結合實例形式分析了微信小程序實現貪吃蛇游戲功能的相關界面布局與代碼邏輯操作技巧,并附帶源碼供讀者下載參考,需要的朋友可以參考下
    2018-01-01
  • JS仿百度自動下拉框模糊匹配提示

    JS仿百度自動下拉框模糊匹配提示

    這篇文章主要介紹了JS仿百度自動下拉框模糊匹配提示 的相關資料,需要的朋友可以參考下
    2016-07-07
  • JavaScript腳本性能優(yōu)化注意事項

    JavaScript腳本性能優(yōu)化注意事項

    本文總結了我在JavaScript編程中所找到的提高JavaScript運行性能的一些方法,其實這些經驗都基于幾條原則
    2008-11-11
  • JavaScript實現99乘法表及隔行變色實例代碼

    JavaScript實現99乘法表及隔行變色實例代碼

    最近做了個項目是要求實現99乘法表隔行變色,本文給大家分享通過多種方式實現js 99 乘法表,感興趣的朋友一起看看吧
    2016-02-02
  • clipboard.js在移動端復制失敗的解決方法

    clipboard.js在移動端復制失敗的解決方法

    最近在使用clipboard.js碰到的一個小問題,通過查找相關資料解決了,所以下面這篇文章主要給大家介紹了關于clipboard.js在移動端復制失敗的解決方法,需要的朋友可以參考借鑒,下面來一起學習學習吧
    2018-06-06
  • 23個Javascript彈出窗口特效整理

    23個Javascript彈出窗口特效整理

    23個Javascript彈出窗口特效,需要的朋友可以參考下。
    2011-02-02
  • 解決JS使用fill()進行數組填充遇到的問題

    解決JS使用fill()進行數組填充遇到的問題

    最近在做算法題時,遇到需要創(chuàng)建二維數組并進行初始化的情況,剛開始我使用的是 new Array(n).fill(new Array(n).fill('.')) 進行二維數組的初始化,但無論怎樣我都通不過測試用例,所以本文就給大家詳細的介紹了如何解決這類問題以及將js中的fill(方法重學一下
    2023-09-09
  • JavaScript常用的工具函數分享

    JavaScript常用的工具函數分享

    這篇文章主要介紹了JavaScript常用的工具函數分享,JavaScript?是一種具有函數優(yōu)先的輕量級,解釋型或即時編譯型的編程語言,下文詳細介紹需要的小伙伴可以參考一下
    2022-03-03
  • JS繪制微信小程序畫布時鐘

    JS繪制微信小程序畫布時鐘

    微信小程序官方組件也提供了畫布功能,下面分享一下如何創(chuàng)建微信小程序畫布時鐘
    2016-12-12

最新評論