【axios源碼】請求取消的實(shí)現(xiàn)

一垮耳、如何設(shè)計(jì)一個(gè)取消請求的功能

取消請求是通過xhr.abort()這樣實(shí)現(xiàn)的。但是不可能將xhr變量暴露出來弃酌,因此在ajax請求的內(nèi)部必須有一個(gè)函數(shù)是取消請求的氨菇,通過這個(gè)函數(shù)取消請求達(dá)到隱藏xhr的目的儡炼。也可以直接是xhr.abort函數(shù),顯然這樣就無法對取消請求的過程無法控制了查蓉。

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

接下來乌询,這個(gè)函數(shù)怎么傳遞出去? 本來這個(gè)函數(shù)的控制權(quán)在封裝的ajax函數(shù)里面豌研,現(xiàn)在我們想把這個(gè)控制權(quán)轉(zhuǎn)交給調(diào)用者妹田。一種很巧妙的方式是通過函數(shù)傳遞出去,這要求初始化ajax請求時(shí)傳過來一個(gè)函數(shù)去接收cancelRequest函數(shù)鹃共。

初始化時(shí)

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

封裝時(shí)

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("請求出錯(cuò)");xhr = null}
        xhr.ontimeout = function(){reject("請求超時(shí)");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);
    })
}

這里有一點(diǎn)需要注意的是當(dāng)請求取消的時(shí)候鬼佣,status為0,而且首先觸發(fā)的onreadystatechange函數(shù)霜浴,為了不讓程序在這里reject晶衷,需要加上判斷if (xhr.status === 0)∫趺希基本上取消請求的功能已經(jīng)可以工作了晌纫,但是這么做有一個(gè)缺點(diǎn),程序直接在onabort函數(shù)中reject出去了永丝,無法包含請求取消的原因锹漱,因?yàn)閤hr.abort()后面的reject無法工作。

二慕嚷、請求分發(fā)

請求分發(fā)過程對取消請求的實(shí)現(xiàn)極為重要哥牍,細(xì)節(jié)見axios流程分析1.6節(jié)。前面那樣實(shí)現(xiàn)的缺點(diǎn)
這個(gè)函數(shù)捕獲到異常之后會判斷是否是【請求取消】的異常喝检,如果不是會嘗試拋出【請求取消】的異常嗅辣。而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請求內(nèi)的abort函數(shù)傳遞出去,是給cancelToken賦予一個(gè)函數(shù)類型的值抠忘,并調(diào)用它撩炊,把cancel函數(shù)作為它的參數(shù)。理解取消請求核心邏輯需要注意兩點(diǎn):

  1. CancelToken怎么和config.cancelToken傳遞信息
  2. http請求內(nèi)部崎脉,abort函數(shù)怎么和CancelToken交互

因此這個(gè)部分需要做好和兩邊數(shù)據(jù)傳遞拧咳。
目前的情況是有三個(gè)函數(shù):cancel、executor囚灼、abort骆膝,我希望cancel調(diào)用之后立即調(diào)用abort祭衩,可以按照下面的寫法。在xhr函數(shù)中把真正能夠取消請求的abort函數(shù)傳遞給CancelToken存起來阅签,然后CancelToken初始化的時(shí)候把包裝abort函數(shù)的cancel函數(shù)作為參數(shù)傳遞給外部的函數(shù)掐暮。

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函數(shù)中
function xhr(config){
//...
 if (config.cancelToken) {
      config.cancelToken.real_cancel  = function(){
        if (!request) {
            return;
        }
        request.abort();
        request = null;
      }
}
//...
}

這樣做有一個(gè)缺陷,取消請求是異步進(jìn)行政钟,如果我想在取消請求之后立即做某件事是做不到的路克。所以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 函數(shù)中
    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函數(shù)傳遞給外部函數(shù)养交,只有cancel函數(shù)被調(diào)用了精算,CancelToken的promise才resolve,從而執(zhí)行then里面的方法碎连。在xhr函數(shù)中將取消請求的函數(shù)早就放到then里面去了灰羽,只是當(dāng)時(shí)不會執(zhí)行,等到cancel調(diào)用之后才執(zhí)行鱼辙。

四廉嚼、對于取消請求的方法,這個(gè)部分我僅僅看懂了座每,但是還不能將源碼中這種技巧靈活運(yùn)用前鹅,所以這個(gè)部分用語言描述起來對我來說非常困難。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末峭梳,一起剝皮案震驚了整個(gè)濱河市舰绘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葱椭,老刑警劉巖捂寿,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異孵运,居然都是意外死亡秦陋,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門治笨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驳概,“玉大人,你說我怎么就攤上這事旷赖∷秤郑” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵等孵,是天一觀的道長稚照。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么果录? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任上枕,我火速辦了婚禮,結(jié)果婚禮上弱恒,老公的妹妹穿的比我還像新娘辨萍。我一直安慰自己,他們只是感情好斤彼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布分瘦。 她就那樣靜靜地躺著,像睡著了一般琉苇。 火紅的嫁衣襯著肌膚如雪嘲玫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天并扇,我揣著相機(jī)與錄音去团,去河邊找鬼。 笑死穷蛹,一個(gè)胖子當(dāng)著我的面吹牛土陪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肴熏,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼鬼雀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蛙吏?” 一聲冷哼從身側(cè)響起源哩,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸦做,沒想到半個(gè)月后励烦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泼诱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年坛掠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片治筒。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屉栓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耸袜,到底是詐尸還是另有隱情系瓢,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布句灌,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏胰锌。R本人自食惡果不足惜骗绕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望资昧。 院中可真熱鬧酬土,春花似錦、人聲如沸格带。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叽唱。三九已至屈呕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棺亭,已是汗流浹背虎眨。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留镶摘,地道東北人嗽桩。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像凄敢,于是被迫代替她去往敵國和親碌冶。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容