libin1991 / libin_Blog

爬虫-博客大全
https://libin.netlify.com/
124 stars 17 forks source link

axios源码分析——取消请求 #575

Open libin1991 opened 6 years ago

libin1991 commented 6 years ago

之前分析了两篇文章

这篇文章,来分析下取消请求是怎么实现的,先从一个简单的取消请求的例子开始:

var CancelToken = axios.CancelToken;
var source = CancelToken.source();
axios.get('/get?name=xmz', {
    cancelToken : source.token
}).then((response)=>{
    console.log('response', response)
}).catch((error)=>{
    if(axios.isCancel(error)){
        console.log('取消请求传递的消息', error.message)
    }else{
        console.log('error', error)
    }
})
// 取消请求
source.cancel('取消请求传递这条消息');

这就是一个简单的取消请求的例子,那么就从最开始的axios.CancelToken来看,先去axios/lib/axios.js文件中。

axios.CancelToken = require('./cancel/CancelToken');

不费吹灰之力,就找到了CancelToken,在例子中我们调用了source方法,那么就去axios/lib/cancel/CancelToken.js文件中看看这个source方法到底是干什么的?

CancelToken.source = function(){
    var cancel;
    var token = new CancelToken(function executor(c) {
        cancel = c
    })
    return {
        token : token,
        cancel : cancel
    }
}

source方法很简单,就是返回一个具有token和cancel属性的对象,但是token和cancel都是通过CancelToken这个构造函数来的,那么还在这个文件中向上看,找到CancelToken函数。

function CancelToken (executor){
    // ...
    // 判断executor是一个函数,不然就报错
    var resolvePromise;
    this.promise = new Promise(function(resolve){
        resolvePromise = resolve;
    })
    var token = this;
    // 以上token现在有一个promise属性,是一个未成功的promise对象;
    executor(function cancel(message){
        if(token.reason){
            return;
        }
        token.reason = new Cancel(message);
        resolvePromise(token.reason);
    })
    // 这个cancel函数就是 上面函数中的cancel,也就是source.cancel;
}

现在知道了source.cancel是一个函数,souce.token是一个实例化对象,暂时就知道这些,继续看文章最开始的例子,接下来是去发送请求了,最下面还有一行代码是执行souce.cancel();

souce.cancel就是用来触发取消请求的函数。

现在再回头来看,上面的cancel函数,cancel执行,给token加了一个reason属性,那么看下这个reason属性是什么吧,看下这个Cancel构造函数,在axios/lib/cancel/Cancel.js文件中

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

Cancel特别简单就是给实例化对象添加一个message属性,所以现在token.reason是一个具有message属性的对象了。

继续回到cancel函数中,resolvePromise函数执行了,那么token.promise对象,这个原本未变成,成功状态的promise,变成了成功状态了,并且将token.reason对象传递过去了。

简单总结一下,执行取消函数,就是让token的promise的状态变成了成功;

好了,突然发现分析中断了,变成成功状态又怎样了,怎么取消的呢?虽然现在的同步代码都执行完了,但是请求还没发送出去呢,我们还要去看发送请求的函数,发送请求的过程已经在之前的文章中分析过了,传送门,戳这里

在分析发送请求之前,再看下最开始的例子,和最普通的发送一个get请求还是有一点区别的,配置对象中多了,一个cancelToken的属性,值是token,到底起了什么作用呢,去axios/lib/adapters/xhr.js中一探究竟(这里只截取其中关于cancelToken的部分)。

// 在发送请求之前,验证了cancelToken,看来此处就是用来取消请求的;
if(config.cancelToken){
    // 具体是如何取消的,是在这个判断内定义的;
    config.cancelToken.promise.then(function(cancel){
        request.abort();
        reject(cancel);
        request = null;
    })
}
// 发送请求
request.send(requestData);

仔细看这只是一个promise的then函数,只有在promise的状态变成成功后才会执行,而刚才我们分析了,cancel就是让这个promise的状态变成成功,所以如果执行了,取消请求的函数,这个then就会执行,取消发送请求,并且把发送请求的promise变成reject,被axiox.get().catch()捕获;

流程已经清楚了,最后再总结一下:

执行cancel是让token的promise变成成功,在真正发送请求之前,验证token.promise的状态是否已经变了,如果变了,就取消请求,就是这样一个简单的思想来进行取消请求的。