前端重复请求,轻松搞定

795 阅读2分钟

简介

当页面复杂时,出现重复请求的概率还是很大的,如何比较优雅的处理,是我们必须要考虑的,这里提供了一种方案,个人认为是比较好的一个方案。可以作为参考。

方案

重复请求阻塞,等待相同请求的返回值

当多个同样的请求发出时,只有第一个会真正到达后端,其余几个阻塞,当第一个请求返回时,将返回值赋给其他正在阻塞的请求。

具体方法就是若判断有相同的请求正在执行中,将返回request请求返回的promise中的resolve和reject函数保存下来,待相同的请求返回,执行保存的resolve或reject函数,将返回值当做参数传入。

代码实现

// request.js
export default function request(
   url,
   {
     method = 'post',
     timeout = TIMEOUT,
     prefix = HOME_PREFIX,
     data = {},
     headers = {},
     dataType = 'json',
     onUploadProgress,
   }
 ) {
   return new Promise((resolve, reject) => {
     const baseURL = autoMatchBaseUrl(prefix)
     headers = {
       // 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
       'Content-Type': 'application/json; charset=UTF-8',
       ...headers,
     }
 
     const defaultConfig = {
       baseURL,
       url,
       method,
       params: data,
       data: data,
       timeout,
       headers,
       responseType: dataType,
     }
 
     const requestingKey = generatePendingKey(defaultConfig)
 
     // 有同样的请求正在执行,等待执行完一起返回
     if (hasPendings(requestingKey)) {
       addPending(requestingKey, resolve, reject)
 
       console.log('requestingKey', requestingKey, getPendings(requestingKey))
     } else {
       // 正常请求
       addPending(requestingKey, resolve, reject)
 
       return axios(defaultConfig).then(
         function(res) {
           // 当页面切换时,会清除pending,所以这里获取到的值可能为空。
           const pendings = getPendings(requestingKey)
 
           if (pendings) {
             pendings.forEach((item) => item.resolve(res))
             removePending(requestingKey)

             // 相关页面逻辑
           }
         },
         function(err) {
           // 当页面切换时,会清除pending,所以这里获取到的值可能为空。
           const pendings = getPendings(requestingKey)
 
           if (pendings) {
             pendings.forEach((item) => item.reject(err))
             removePending(requestingKey)

             // 相关页面逻辑
           }
         }
       )
     }
   })
 }
 
// pending.js
import Qs from 'qs'
import { isEmpty } from '@/common/help'
const requestingMap = new Map()

/**
 * 添加请求
 * @param {Object} config
 */
const addPending = (requestingKey, resolve, reject) => {
  if (hasPendings(requestingKey)) {
    requestingMap.get(requestingKey).push({
      resolve,
      reject,
    })
  } else {
    requestingMap.set(requestingKey, [
      {
        resolve,
        reject,
      },
    ])
  }
}

/**
 * 移除请求
 * @param {Object} config
 */
const removePending = (requestingKey) => {
  requestingMap.delete(requestingKey)
}

const getPendings = (requestingKey) => {
  return requestingMap.get(requestingKey)
}

const hasPendings = (requestingKey) => {
  return requestingMap.has(requestingKey)
}

/**
 * 合成pending的key方法
 */
const generatePendingKey = (config) => {
  const keys = [config.method, config.url, Qs.stringify(config.params)]

  if (!isEmpty(config.data)) {
    keys.push(Qs.stringify(config.data))
  }

  return keys.join('&')
}

export {
  getPendings,
  hasPendings,
  addPending,
  removePending,
  generatePendingKey,
}