Axios之取消重複請求

借用掘友(橙某人)的前言解釋: 提及這個重複請求,感受用到得比較少,心理總有怎麼一種想法就算多請求一次又能怎麼樣,服務器會塌嗎?頁面會掛嗎?明顯不會嘛,不要大驚小怪,哈哈哈。再說沒事怎麼會多發重複的請求呢?不可能的。前端

image.png

並且作取消重複請求操做,其實取消後的請求仍是有可能會到達了後端,只是前端瀏覽器不處理而已,可是呢,哎,咱們仍是得作作工做,不,非作不可,所謂以防萬一,嚴謹,程序猿須要嚴謹!!!vue

發生重複請求的場景通常有這兩個:ios

  • 快速連續點擊一個按鈕,若是這個按鈕未進行控制,就會發出重複請求,假設該請求是生成訂單,那麼就有產生兩張訂單了,這是件可怕的事情。固然通常前端會對這個按鈕進行狀態處理控制,後端也會有一些冪等控制處理策略啥的,這是個假設場景,但也可能會發生的場景。
  • 對於列表數據,可能有tab狀態欄的頻繁切換查詢,若是請求響應很慢,也會產生重複請求。固然如今不少列表都會作緩存,如Vue中用 <keep-alive />

正言:axios

判斷重複請求並儲存進隊列

首先咱們要收集請求中的接口並判斷哪些請求是重複請求,咱們才能取消它,那麼如何判斷呢?很簡單,只要是請求地址、請求方式、請求參數同樣,那麼咱們就能認爲是同樣的。而咱們要存儲的隊列裏面的數據結構很明顯應該是以鍵值對的形式來存儲,這裏面咱們選擇 Map 對象來操做。後端

// axios.js
const pendingMap = new Map();

/**
 * 生成每一個請求惟一的鍵
 * @param {*} config 
 * @returns string
 */
function getPendingKey(config) {
  let {url, method, params, data} = config;
  if(typeof data === 'string') data = JSON.parse(data); // response裏面返回的config.data是個字符串對象
  return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&');
}

/**
 * 儲存每一個請求惟一值, 也就是cancel()方法, 用於取消請求
 * @param {*} config 
 */
function addPending(config) {
  const pendingKey = getPendingKey(config);
  config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
    if (!pendingMap.has(pendingKey)) {
      pendingMap.set(pendingKey, cancel);
    }
  });
}
複製代碼

取消重複請求並出刪除隊列

// axios.js
/**
 * 刪除重複的請求
 * @param {*} config 
 */
function removePending(config) {
  const pendingKey = getPendingKey(config);
  if (pendingMap.has(pendingKey)) {
     const cancelToken = pendingMap.get(pendingKey);
     cancelToken(pendingKey);
     pendingMap.delete(pendingKey);
  }
}
複製代碼

添加攔截器

// axios.js
function myAxios(axiosConfig) {
  const service = axios.create({
    baseURL: 'http://localhost:8888', // 設置統一的請求前綴
    timeout: 10000, // 設置統一的超時時長
  });

  service.interceptors.request.use(
    config => {
      removePending(config);
      addPending(config);
      return config;
    }, 
    error => {
      return Promise.reject(error);
    }
  );

  service.interceptors.response.use(
    response => {
      removePending(response.config);
      return response;
    },
    error => {
      error.config && removePending(error.config);
      return Promise.reject(error);
    }
  );

  return service(axiosConfig)
}
複製代碼

咱們在上面提到的 getList() 方法裏面簡單模擬連續發出了三次重複請求,而後把瀏覽器設置成3G模式,就能看到效果啦。瀏覽器

// App.vue
function getList() {
  getListAPI().then(res => {console.log(res)})
  setTimeout(() => {
    getListAPI().then(res => {console.log(res)})
  }, 200);
  setTimeout(() => {
    getListAPI().then(res => {console.log(res)})
  }, 400);
}
複製代碼

image.png

須要注意,上面說了取消正在請求中的接口,說明這接口有可能已經到達後端了,只是後端響應慢,因此若是你的接口響應比較快的話,就很難看到效果;若是你是本身搭建的服務,只要經過接口返回時延時下就能夠看到效果;又或者經過瀏覽器的network調整網絡速度也能夠哦。image.png緩存

對於取消後的請求咱們也應該有個合理的處理,不能就無論了,儘量的達到代碼可控的底部,它會被歸類到異常裏面,下面會說到( ^ω^ )。服務器

配置化(有的接口須要重複請求:config.repeat_request_cancel)

之因此弄成配置化取消重複請求,是由於可能存在一些特殊變態的場景狀況,是須要重複請求,如輸入實時搜索、實時更新數據等,反正就是可能存在吧。( ̄y▽ ̄)~*markdown

// request interceptor
service.interceptors.request.use(
  config => {
    if (config.method === 'upload') {
      config = {
        ...config,
        ...{
          method: 'post',
          data: config.data,
          methodType: 'upload',
          headers: {
            'Content-Type': 'multipart/form-data' // 須要指定上傳的方式
          },
          transformRequest: [function() {
            return config.data
          }]
        }
      }
    }
    removePending(config)
    config.repeat_request_cancel && addPending(config)
    return config
  },
  error => {
    // do something with request error
    console.log('error', error) // for debug
    return Promise.reject(error)
  }
)
複製代碼

咱們在上面增長了一個自定義配置的參數,如今每一個API方法就能擁有兩個參數,第一個參數傳遞的是axios本來的一些配置,第二個參數就是咱們本身的一些自定義參數了,如咱們定義 repeat_request_cancel 來控制是否開啓取消重複請求的功能。後續更多功能,咱們也能添加進其中,至關於可定製化每一個API方法,是否是很棒!!!網絡

// 營銷信息管理-列表
// 配置repeat_request_cancel的Boolean類型來判斷
export function getProductElementInfoUrlByReq(data, loading = true) {
  return request({
    url: '/xxxxx/xxxxxxx',
    method: 'post',
    data: data,
    loading,
    repeat_request_cancel: true
  })
}
複製代碼

over!

相關文章
相關標籤/搜索