axios原始碼分析——取消請求

m_ing發表於2018-06-18

之前分析了兩篇文章

這篇文章,來分析下取消請求是怎麼實現的,先從一個簡單的取消請求的例子開始:

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的狀態是否已經變了,如果變了,就取消請求,就是這樣一個簡單的思想來進行取消請求的。

相關文章