Axios取消重复请求

本文详细介绍了如何使用Axios的CancelToken来取消重复请求,以避免用户快速操作导致的多余请求。通过在请求拦截器中检查并取消重复请求,实现了优化用户体验和提高应用性能的目标。同时,解释了CancelToken的工作原理,展示了其实现取消请求的内部机制。
摘要由CSDN通过智能技术生成

Axios取消重复请求

背景:请求的响应时间存在不确定性,请求次数过多时,有可能较早发起的请求会较晚响应。那么我们需要设计一套机制,确保较晚发起的请求可以在客户端就取消掉较早发起的请求。比如重复的 post 请求可能会导致服务端产生多个日志记录,而且会影响加载速度,进一步影响用户体验。例如:

  • 页面按钮——用户点击"查询"按钮会发起一个AJAX的GET请求,但是如果不做限制,当用户快速点击时,会重复发出请求,当查询数据量较大时,会有明显延时;
  • Tab切换——用户在“全部”、“待检查”、“待报告”、“已完成”之间来回切换,在上一个请求处于pending,下一个请求已经发生,并等待上一个请求完成

一、取消请求的方式

对于Axios来说,可以使用内部提供的CancelToken来取消请求

// 官网示例
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function (thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // handle error
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');

也可以使用构造函数来创建CancelToken

const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/111', {
    CancelToken: new CancelToken(function executor(c) {
        cancel = c;
    })
})

注意:get请求,cancelToken 放第二个参数;post请求,cancelToken 放第三个参数

二、判断重复请求

可以用”请求方式+请求URL+请求参数“来作为是否重复请求的判断依据。根据这三样可以生成一个唯一的key,为每个请求创建一个专属的CancelToken,然后把key和cancel函数以键值对的形式保存到Map对象中,使用 Map 的好处是可以快速的判断是否有重复的请求

import qs from 'qs'

const pendingRequest = new Map();
// GET -> params;POST -> data
const requestKey = [method, url, qs.stringify(params), qs.stringify(data)].join('&'); 
const cancelToken = new CancelToken(function executor(cancel) {
  if(!pendingRequest.has(requestKey)){
    pendingRequest.set(requestKey, cancel);
  }
})

当出现重复请求的时候,我们就可以使用 cancel 函数来取消前面已经发出的请求,在取消请求之后,我们还需要把取消的请求从 pendingRequest 中移除。

三、取消重复请求

因为我们需要对所有的请求都进行处理,所以我们可以考虑使用 Axios 的拦截器机制来实现取消重复请求的功能

  • 请求拦截器:在请求发送前统一执行某些操作,比如在请求头中添加 token 字段。
  • 响应拦截器:在接收到服务器响应后统一执行某些操作,比如发现响应状态码为 401 时,自动跳转到登录页。

3.1 定义辅助函数

  • generateReqKey :根据当前请求信息生成Key;
function generateReqKey(config) {
  const { method, url, params, data } = config;
  return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}
  • addPendingRequest :把当前请求信息添加到pendingRequest对象中;
const pendingRequest = new Map();
function addPendingRequest(config) {
  const requestKey = generateReqKey(config);
  config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
    if (!pendingRequest.has(requestKey)) {
       pendingRequest.set(requestKey, cancel);
    }
  });
}
  • removePendingReques :检查是否存在重复请求,若存在则取消已发的请求;
function removePendingRequest(config) {
  const requestKey = generateReqKey(config);
  if (pendingRequest.has(requestKey)) {
     const cancelToken = pendingRequest.get(requestKey);
     cancelToken(requestKey);
     pendingRequest.delete(requestKey);
  }
}

3.2 设置请求拦截器

axios.interceptors.request.use(
  function (config) {
    removePendingRequest(config); // 检查是否存在重复请求,若存在则取消已发的请求
    addPendingRequest(config); // 把当前请求信息添加到pendingRequest对象中
    return config;
  },
  (error) => {
     return Promise.reject(error);
  }
);

3.3 设置响应拦截器

axios.interceptors.response.use(
  (response) => {
     removePendingRequest(response.config); // 从pendingRequest对象中移除请求
     return response;
   },
   (error) => {
      removePendingRequest(error.config || {}); // 从pendingRequest对象中移除请求
      if (axios.isCancel(error)) {
        console.log("已取消的重复请求:" + error.message);
      } else {
        // 添加异常处理
      }
      return Promise.reject(error);
   }
);

最终呈现的效果就是

在这里插入图片描述

四、CancelToken 的工作原理

在node_modules/axios/lib/cancel/CancelToken.js文件中:

function CancelToken(executor) { // 参数executor执行器  
	// 参数类型判断为函数  
	if (typeof executor !== 'function') {
		throw new TypeError('executor must be a function.');
	}
	var resolvePromise;
	// 实例挂载一个promise,这个promise会在变量resolvePromise执行后resolved
	this.promise = new Promise(function promiseExecutor(resolve) {
		resolvePromise = resolve;
	});
	var token = this;  // 执行器执行,将函数cancel传递到外界
	executor(function cancel(message) { // 设置cancel对象
		if (token.reason) {
			// Cancellation has already been requested
			return;
		}
		token.reason = new Cancel(message);    // 外界可以通过执行resolvePromise来将该token的promise置为resolved
		resolvePromise(token.reason);
	});
}
CancelToken.source = function source() {
	var cancel;
	var token = new CancelToken(function executor(c) {
		cancel = c;
	});
	return {
		token: token,
		cancel: cancel
	}
}

由以上代码可知,cancel 对象是一个函数,当我们调用该函数后,会创建 Cancel 对象并调用 resolvePromise 方法。该方法执行后,CancelToken 对象上 promise 属性所指向的 promise 对象的状态将变为 resolved,进而执行下方代码

// lib/adapters/xhr.js
if (config.cancelToken) {  
	config.cancelToken.promise.then(function onCanceled(cancel) {    
		if (!request) { return; }    
		request.abort(); // 取消请求    
		reject(cancel);    
		request = null;  
	});
}

总结

使用此方法后,当出现重复请求时,之前已发送且未完成的请求会被取消掉。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值