美文网首页axios源码解析
【axios源码】请求取消的实现

【axios源码】请求取消的实现

作者: 可以秀但没必要 | 来源:发表于2020-01-27 05:37 被阅读0次

一、如何设计一个取消请求的功能

取消请求是通过xhr.abort()这样实现的。但是不可能将xhr变量暴露出来,因此在ajax请求的内部必须有一个函数是取消请求的,通过这个函数取消请求达到隐藏xhr的目的。也可以直接是xhr.abort函数,显然这样就无法对取消请求的过程无法控制了。

function cancelRequest(){
    if(xhr==null){
        return
    }
    xhr.abort()
    reject("请求取消")
    xhr = null
}

接下来,这个函数怎么传递出去? 本来这个函数的控制权在封装的ajax函数里面,现在我们想把这个控制权转交给调用者。一种很巧妙的方式是通过函数传递出去,这要求初始化ajax请求时传过来一个函数去接收cancelRequest函数。

初始化时

var cancel = null
rquest({
   ...
  cancelToken:function(c){
      cancel = c
  }
 ...
})

封装时

function rquest(config){
...
    if(typeof config.cancelToken == "function"){
        config.cancelToken(cancelRequest)
    }
...
}

完整代码如下:

function xhr(config){
    return new Promise((resolve,reject)=>{
        var {url,data,headers,method,cancelToken} = config
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange=function(){
            if (xhr.readyState!=4){
                return
            }
            if (xhr.status === 0) {
                return;
            }
            if(xhr.status >= 200 && xhr.status < 300){
                //TODO::
                resolve(xhr.responseText)
            }else{
                reject("请求异常")
            }
            xhr = null
        }
        xhr.onabort = function(){
            if(!xhr){return}
            reject("请求取消");
            xhr = null
        }
        xhr.onerror = function(){reject("请求出错");xhr = null}
        xhr.ontimeout = function(){reject("请求超时");xhr = null}
        function cancelRequest(message){
            if(xhr==null){
                return
            }
            xhr.abort()
            reject(message)
            xhr = null
        }
        if(typeof cancelToken == "function"){
            cancelToken(cancelRequest)
        }
        xhr.open(method,url,true);
        xhr.send(data);
    })
}

这里有一点需要注意的是当请求取消的时候,status为0,而且首先触发的onreadystatechange函数,为了不让程序在这里reject,需要加上判断if (xhr.status === 0)。基本上取消请求的功能已经可以工作了,但是这么做有一个缺点,程序直接在onabort函数中reject出去了,无法包含请求取消的原因,因为xhr.abort()后面的reject无法工作。

二、请求分发

请求分发过程对取消请求的实现极为重要,细节见axios流程分析1.6节。前面那样实现的缺点
这个函数捕获到异常之后会判断是否是【请求取消】的异常,如果不是会尝试抛出【请求取消】的异常。而axios为取消请求封装的类中包含了这些功能。

三、axios取消请求解析

3.1 取消信息的封装,它包含可以识别出【取消请求】异常的属性__CANCEL__

function Cancel(message) {
  this.message = message;
}

Cancel.prototype.toString = function toString() {
  return 'Cancel' + (this.message ? ': ' + this.message : '');
};

Cancel.prototype.__CANCEL__ = true;

3.2 取消异常类型判断,对判断取消异常的封装

function isCancel(value) {
  return !!(value && value.__CANCEL__);
};

3.3 取消请求核心逻辑

在最开始,我为了将http请求内的abort函数传递出去,是给cancelToken赋予一个函数类型的值,并调用它,把cancel函数作为它的参数。理解取消请求核心逻辑需要注意两点:

  1. CancelToken怎么和config.cancelToken传递信息
  2. http请求内部,abort函数怎么和CancelToken交互

因此这个部分需要做好和两边数据传递。
目前的情况是有三个函数:cancel、executor、abort,我希望cancel调用之后立即调用abort,可以按照下面的写法。在xhr函数中把真正能够取消请求的abort函数传递给CancelToken存起来,然后CancelToken初始化的时候把包装abort函数的cancel函数作为参数传递给外部的函数。

function CancelToken(executor){
    this.real_cancel = null
    var token = this;
    executor(function cancel(message) {
        if (token.reason) {
            return;
        }
        token.reason = new Cancel(message);
        token.real_cancel && token.real_cancel()
    });
}

//xhr函数中
function xhr(config){
//...
 if (config.cancelToken) {
      config.cancelToken.real_cancel  = function(){
        if (!request) {
            return;
        }
        request.abort();
        request = null;
      }
}
//...
}

这样做有一个缺陷,取消请求是异步进行,如果我想在取消请求之后立即做某件事是做不到的。所以axios是用promise来组织这三者之间的顺序。

function CancelToken(executor) {
  if (typeof executor !== 'function') {
    throw new TypeError('executor must be a function.');
  }
  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });
  var token = this;
  executor(function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }
    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}

//xhr 函数中
    if (config.cancelToken) {
      // Handle cancellation
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
          return;
        }

        request.abort();
        // reject(cancel);
        // Clean up request
        request = null;
      });
    }

首先把cancel函数传递给外部函数,只有cancel函数被调用了,CancelToken的promise才resolve,从而执行then里面的方法。在xhr函数中将取消请求的函数早就放到then里面去了,只是当时不会执行,等到cancel调用之后才执行。

四、对于取消请求的方法,这个部分我仅仅看懂了,但是还不能将源码中这种技巧灵活运用,所以这个部分用语言描述起来对我来说非常困难。

相关文章

  • 【axios源码】请求取消的实现

    一、如何设计一个取消请求的功能 取消请求是通过xhr.abort()这样实现的。但是不可能将xhr变量暴露出来,因...

  • axios取消请求的实现

    简述 文档[https://www.axios-http.cn/docs/cancellation]处提到两种撤销...

  • axios如何取消接口请求

    vue项目,如何在axios中取消已经发送的请求呢? 原生js的abort()这个方法 在axios中取消接口请求...

  • Axios源码阅读(三):取消请求

    一、功能介绍 官方文档[https://axios-http.com/docs/cancellation]指出有2...

  • axios 取消请求

    axios文档里介绍的取消axios请求有以下两种方式: 举?:切换路由时,取消上个路由的请求。 其实我们的解决方...

  • axios取消请求?

    前言 最近在项目中遇到一个问题,在连续发送同一请求时,如果第二次请求比第一次请求快,那么实际显示的是第一次请求的数...

  • 基于 axios 实现取消重复请求

    Axios 是一个基于 Promise 的 HTTP 客户端,同时支持浏览器和 Node.js 环境。它是一个优秀...

  • axios取消重复请求实现

    在我们日常开发中,有这样一种场景必须要进行处理,那就是在提交表单的时候,如果很快的重复点击两次,会造成重复请求,第...

  • 防止发起多余http请求的几种方式

    cancelToken 场景: 请求接口数据量过大,结果未返回需要取消接口pending用法:axios取消请求的...

  • 问题总结

    1. 取消axios请求 业务场景:单页应用,希望退出当前页的时候,取消请求 官网: https://github...

网友评论

    本文标题:【axios源码】请求取消的实现

    本文链接:https://www.haomeiwen.com/subject/ywmnzctx.html