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!

相关文章
相关标签/搜索