Axios 取消重复请求、请求失败自动重试、请求缓存思路

218 阅读1分钟
  1. 取消重复请求: 完全相同的接口在上一个pending状态时,自动取消下一个请求
  2. 请求失败自动重试: 接口请求后台异常时候,自动重新发起多次请求,直到达到所设次数
  3. 请求接口数据缓存:接口在设定时间内不会向后台获取数据,而是直接拿本地缓存
cancelRequest: true       // 接口中定义该项则开启取消重复请求功能
retry: 3, retryDelay: 1000  // retry 请求重试次数,retryDelay 两次重试之间的时间间隔
cache: true, setExpireTime: 30000  // cache: true 开启当前接口缓存,setExpireTime 当前接口缓存时限

取消重复请求

完全相同的接口在上一个 pedding 状态时,自动取消下一个请求

  1. 在请求拦截器判断  pendingRequestMap 是否存在当前请求的 key,不存在则把当前请求信息添加到 pendingRequestMap 对象中.
export const generateReqKey = (config) => {

  // 响应的时候,response.config 中的 data 是一个JSON字符串,所以需要转换一下
  if (config && config.data && isJsonStr(config.data)) {
    config.data = JSON.parse(config.data);
  }
  const { method, url, params, data } = config; // 请求方式,参数,请求地址,
  return [method, url, Qs.stringify(params), Qs.stringify(data)].join('&'); // 拼接

}
export const addPendingRequest = (config) => {
  if (config.cancelRequest) {
    const requestKey = getRequestKey(config)
    if (peddingRequest.has(requestKey)) {
      config.cancelToken = new Axios.CancelToken(cancel => {
        // cancel 函数的参数会作为 promise 的 error 被捕获
        cancel(`${config.url} 请求已取消`)
      })
    } else {
      config.cancelToken = config.cancelToken || 
        new Axios.CancelToken(cancel => {
          peddingRequest.set(requestKey, cancel)
        })
    }
  }
}
  1. 响应拦截器正常时从 pendingRequest 对象中移除请求,
// removePendingRequest:检查是否存在重复请求,若存在则取消已发的请求。 
export function removePendingRequest(response) { 
    if (response && response.config && response.config.cancelRequest) { 
        const requestKey = generateReqKey(response.config); 
        // 判断是否有这个 key if (pendingRequest.has(requestKey)) { 
        const cancelToken = pendingRequest.get(requestKey); 
        cancelToken(requestKey); 
        pendingRequest.delete(requestKey); 
   
       } 
     } 
  }

请求失败自动重试

接口请求后台异常时候,自动重新发起多次请求,直到达到所设次数

响应拦截器如果 error,从 pendingRequest 对象中移除请求,如果请求未被取消,则重新发起请求

// 实现 请求错误时重新发送接口
import { isJsonStr } from './commonFuns';
/**
 * @param {失败信息} err
 * @param {实例化的单例} axios
 * @returns
 */
export function againRequest(err, axios) {
    let config = err.config;
    // config.retry 具体接口配置的重发次数
    if (!config || !config.retry) return Promise.reject(err);

    // 设置用于记录重试计数的变量 默认为0
    config.__retryCount = config.__retryCount || 0;

    // 判断是否超过了重试次数
    if (config.__retryCount >= config.retry) {
        return Promise.reject(err);
    }
    // 重试次数
    config.__retryCount += 1;

    // 延时处理
    var backoff = new Promise(function(resolve) {
        setTimeout(function() {
            resolve();
        }, config.retryDelay || 1000);
    });
    // 重新发起axios请求
    return backoff.then(function() {
        // 判断是否是JSON字符串
        // TODO: 未确认config.data再重发时变为字符串的原因
        if (config.data && isJsonStr(config.data)) {
            config.data = JSON.parse(config.data);
        }
        return axios(config);
    });
}

请求缓存

接口在设定时间内不会向后台获取数据,而是直接拿本地缓存。通过回调 cancel 方法 cancel(cacheData) 传递结果到 catch 中。例如:

axios.post("/test/list").then(() => {

}).catch(() => {
    // 此处接收缓存的数据
)
export function requestInterceptor(config, axios) {
  // 开启缓存则保存请求结果和cancel 函数
  if (config.cache) {
    let data = CACHES[`${generateReqKey(config)}`];
    // 这里用于存储是默认时间还是用户传递过来的时间
    let setExpireTime;
    config.setExpireTime ? (setExpireTime = config.setExpireTime) : (setExpireTime = options.expire);
    // 判断缓存数据是否存在 存在的话 是否过期 没过期就返回
    if (data && getNowTime() - data.expire < setExpireTime) {
      config.cancelToken = new Axios.CancelToken(cancel => {
        // cancel 函数的参数会作为 promise 的 error 被捕获
        cancel(data);
      }); // 传递结果到catch中
    }
  }
}

export function responseInterceptor(response) {
  // 返回的code === 200 时候才会缓存下来
  if (response && response.config.cache && response.data.code === 200) {
    let data = {
      expire: getNowTime(),
      data: response
    };

    CACHES[`${generateReqKey(response.config)}`] = data;
  }
}