axios取消请求功能CancelToken原理解析

今天在论坛看到一个取消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 方法是一个静态方法,它返回一个对象,该对象包含 tokencancel 函数。这为用户提供了一个方便的方式来获取 CancelToken 和取消函数。

现在,当用户想要取消请求时,他们会调用 source.cancel 函数,这会触发 CancelToken 中的 executor 函数,进而解析 this.promise 为一个 Cancel 实例。在 Axios 内部,当发送请求时,会检查这个 promise,并在请求被取消时拒绝(reject)相关的 Promise

写在最后:欢迎关注扫码作者微信公众号fever code,获取一手技术分享⛽️
在这里插入图片描述

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值