今天在论坛看到一个取消axios
请求的功能,具体场景是这样的:前端接口以较短时间间隔请求后端接口刷新状态,接口返回“成功”状态之后还有处于pending
状态的请求在排队,如果请求不取消就会导致前端最后获取到的状态出错,这个时候axios的CancelToken
取消请求功能就派上用场了。
需要注意的是CancelToken在v0.22.0
版本中已经被废弃,不应在新项目中使用,需要使用axios取消请求功能请使用AbortController
方式(AbortController
是fetch的原生API,axios从v0.22.0
版本开始支持),本文仅供学习交流之用。顺嘴提一句,欢迎大家关注我的微信公众号fever code
,获取最新技术分享。
技术背景
HTTP请求本身在协议层面上是不可取消的。一旦HTTP请求被发送出去,它就会按照TCP/IP协议栈的规定在网络中传输,直到到达服务器或者因为某种原因(如网络中断)而失败。在这个过程中,客户端(如浏览器或应用程序)无法直接取消或中断这个请求。
然而,在实际应用中,我们可以通过一些机制来间接实现取消HTTP请求的效果。例如,在前端开发中,我们可以使用JavaScript来监听用户的行为(如点击取消按钮),然后在合适的时候停止处理HTTP请求的响应。这并不意味着请求本身被取消了,而是客户端不再关心这个请求的结果,也不会对其进行任何处理。
实现原理
首先,我们知道Axios
的请求是基于Promise
的,也就是说,每一个Axios
请求都会返回一个Promise
对象。这个Promise
对象代表了异步操作的最终完成(或失败)及其结果值。
然而,Promise
的一个问题是它一旦被创建就不能被取消。这意味着一旦你发起了一个Axios
请求,你就无法直接取消它。为了解决这个问题,Axios
引入了CancelToken
。
CancelToken
是一个对象,它包含一个promise
和一个用于取消请求的函数。当你创建一个 CancelToken
时,你可以将这个token
传递给Axios
请求。如果请求仍在等待中,那么调用 CancelToken
的取消函数将会拒绝与之关联的Promise
,从而取消请求。
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 {
// 处理错误
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
CancelToken原理解析
CancelToken
是 Axios 中的一个重要组件,它允许用户取消未完成的请求。以下是 CancelToken
的简化实现源码:
首先,我们定义 Cancel
类,它表示一个取消操作:
function Cancel(message) {
// 取消操作的原因(消息)
this.message = message;
}
// 重写Cancel类的toString方法,用于将取消操作转换为字符串
Cancel.prototype.toString = function () {
return 'Cancel' + (this.message ? ': ' + this.message : '');
};
// 为Cancel类的实例添加一个标记,用于后续识别取消错误
Cancel.prototype.__CANCEL__ = true;
Cancel
类接收一个消息作为参数,并有一个 toString
方法用于将取消信息转换为字符串。__CANCEL__
属性是一个标记,用于后续识别取消错误。
接下来,我们定义 CancelToken
类:
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
// 定义一个resolvePromise函数,用于在取消时解析promise
var resolvePromise;
// 创建一个新的Promise对象,这个Promise在取消时会被解析
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
// 定义一个变量token,用于引用当前的CancelToken实例
var token = this;
// 调用executor函数,并传入一个取消函数作为参数
executor(function cancel(message) {
// 如果token已经被取消过,则不再重复取消
if (token.reason) {
// CancelToken 已经取消过,不应该再取消
return;
}
// 将取消的原因(Cancel实例)存储在token的reason属性中
token.reason = new Cancel(message);
// 调用resolvePromise函数,解析this.promise为取消的原因
resolvePromise(token.reason);
});
}
// 为CancelToken类定义一个方法,用于在请求发送前检查是否已取消
CancelToken.prototype.throwIfCancelled = function throwIfCancelled() {
// 如果token已经被取消(reason属性存在),则抛出取消的原因
if (this.reason) {
throw this.reason;
}
};
// 为CancelToken类定义一个静态方法source,用于生成token和取消函数
CancelToken.source = function source() {
var cancel;
// 创建一个新的CancelToken实例,并将取消函数作为参数传递给executor
var token = new CancelToken(function executor(c) {
// 将传入的取消函数赋值给cancel变量
cancel = c;
});
// 返回一个对象,包含生成的token和取消函数
return { token: token, cancel: cancel };
};
在 CancelToken
类中:
executor
是一个函数,它接收一个取消函数作为参数。当需要取消请求时,调用这个函数。this.promise
是一个新的Promise
对象,它会在取消函数被调用时解析(resolve)为Cancel
实例。token.reason
存储取消的原因(Cancel
实例)。
CancelToken.source
方法是一个静态方法,它返回一个对象,该对象包含 token
和 cancel
函数。这为用户提供了一个方便的方式来获取 CancelToken
和取消函数。
现在,当用户想要取消请求时,他们会调用 source.cancel
函数,这会触发 CancelToken
中的 executor
函数,进而解析 this.promise
为一个 Cancel
实例。在 Axios 内部,当发送请求时,会检查这个 promise
,并在请求被取消时拒绝(reject)相关的 Promise
。
写在最后:欢迎关注扫码作者微信公众号fever code
,获取一手技术分享⛽️