Axios核心原理解析

使用者 - 導(dǎo)入axios

import axios from 'axios';

axios源碼 - 創(chuàng)建axios實例/返回axios實例

// axios.js
var axios = createInstance(defaults);
module.exports.default = axios;

我們導(dǎo)入的是利用默認(rèn)配置生成的一個axios實例

使用者 - 配置實例基本配置

const config = {
    baseURL: 'http://localhost:3344'
}

const request = axios.create(config)

axios源碼 - 合并實例配置和默認(rèn)配置/創(chuàng)建新的實例并返回

axios.create = function create(instanceConfig) {
    return createInstance(mergeConfig(axios.defaults, instanceConfig));
};

優(yōu)先級順序:請求配置>實例配置>默認(rèn)配置

使用者 - 得到新實例福青,命名為request

const request = axios.create(config)

使用者 - 設(shè)置請求/響應(yīng)攔截

// 添加請求攔截器
request.interceptors.request.use(function (config) {
    // 在發(fā)送請求之前做些什么
    console.log('請求攔截1', config);
    return config;
}, function (error) {
    // 對請求錯誤做些什么
    console.log("請求攔截出錯1")
    return Promise.reject(error);
});

// 添加請求攔截器
request.interceptors.request.use(function (config) {
    // 在發(fā)送請求之前做些什么
    console.log('請求攔截2', config);
    return config;
}, function (error) {
    // 對請求錯誤做些什么
    console.log("請求攔截出錯2")
    return Promise.reject(error);
});

// 添加響應(yīng)攔截器
request.interceptors.response.use(function (response) {
    // 對響應(yīng)數(shù)據(jù)做點(diǎn)什么
    console.log("攔截響應(yīng)1");
    response = { message: "響應(yīng)數(shù)據(jù)被我替換了窗市,啊哈哈哈" }
    return response;
}, function (error) {
    // 對響應(yīng)錯誤做點(diǎn)什么
    console.log(error);
    console.log("攔截出錯1");
    return Promise.reject(error);
});

axios源碼 - 存儲請求/響應(yīng)攔截到interceptors

// 攔截器構(gòu)造函數(shù)
function InterceptorManager() {
    this.handlers = [];
}
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
    this.handlers.push({
        fulfilled: fulfilled,
        rejected: rejected
    });
    return this.handlers.length - 1;
};

// 請求攔截和響應(yīng)攔截均是攔截器的實例化
function Axios(instanceConfig) {
    this.defaults = instanceConfig;
    this.interceptors = {
        request: new InterceptorManager(),
        response: new InterceptorManager()
    };
}

// 用數(shù)組存儲執(zhí)行鏈
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
// 將請求攔截成對推入執(zhí)行鏈的頭部
// 知道這個原理以后,我們就知道在設(shè)置多個請求攔截時,會按照設(shè)置時的順序,倒序處理
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 將響應(yīng)攔截成對推入執(zhí)行鏈的尾部,執(zhí)行時按照設(shè)置時的順序,正序處理
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
});
// 依次成對執(zhí)行,并用新的promise代替舊的promise幔亥,最后返回最新的promise
while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
}
return promise;

重點(diǎn)分析:promise = promise.then(chain.shift(), chain.shift());

var promise = Promise.resolve(config);
while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
}
return promise;

我們將以上代碼,按照流程順序?qū)hile語句根據(jù)實際情況拆解察纯,更能理解以上代碼的精髓

var promise = Promise.resolve(config);

promise = promise.then(
    function (config) {
        // 在發(fā)送請求之前做些什么
        console.log('請求攔截2', config);
        return config;
    },
    function (error) {
        // 對請求錯誤做些什么
        console.log("請求攔截出錯2")
        return Promise.reject(error);
    }
)

promise = promise.then(
    function (config) {
        // 在發(fā)送請求之前做些什么
        console.log('請求攔截1', config);
        return config;
    },
    function (error) {
        // 對請求錯誤做些什么
        console.log("請求攔截出錯1")
        return Promise.reject(error);
    }
)

promise = promise.then(
    dispatchRequest,
    undefined
)

promise = promise.then(
    function (response) {
        // 對響應(yīng)數(shù)據(jù)做點(diǎn)什么
        console.log("攔截響應(yīng)1");
        response = { message: "響應(yīng)數(shù)據(jù)被我替換了帕棉,啊哈哈哈" }
        return response;
    },
    function (error) {
        // 對響應(yīng)錯誤做點(diǎn)什么
        console.log(error);
        console.log("攔截出錯1");
        return Promise.reject(error);
    }
)

return promise;

其實質(zhì)就是一個promise的鏈?zhǔn)秸{(diào)用,如果執(zhí)行過程中沒有調(diào)用Promise.reject饼记,將按照resolve路線走香伴,一旦調(diào)用Promise.reject,將執(zhí)行后面的所有reject函數(shù)

思考1:為什么需要使用者在reject函數(shù)中手動調(diào)用Promise.reject握恳?

思考2:為什么需要使用者在請求攔截resolve函數(shù)中一直return config瞒窒?

取消請求的實現(xiàn)

使用者 - 使用

// src/api.js
export function checkTask (parameter, others) {
  return request({
    url: projectApi.checkTask,
    method: 'post',
    data: parameter,
    ...others
  })
}

// view/components/view.jsx
import request from '@axios/myRequest'
import { checkTask } from '@src/api'
const source = request.CancelToken.source();// 實例化

// 發(fā)出請求
checkTask(formData, { cancelToken: source.token }).then(res=>{
    console.log(res)
})

// 關(guān)閉請求彈窗時,或其他操作乡洼,需要取消請求時
function cancelCheckTask(){
   source.cancel('cancel msg');
}

axios源碼 - axios.CancelToken.source()做了啥

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

創(chuàng)建了一個 CancelToken 實例給 token,

CancelToken 的參數(shù)是一個函數(shù)executor崇裁,

將函數(shù)executor的參數(shù)c再賦值給 cancel

將 { token: token,cancel: cancel } 作為新對象返回

cancel是一個函數(shù),執(zhí)行時將取消帶有當(dāng)前token標(biāo)記的請求

axios源碼 - CancelToken又做了啥

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);
    });
}

創(chuàng)建了一個 Promise , 同時保存 Promise resolve 的具體實現(xiàn)

執(zhí)行上一步傳遞的函數(shù) executor 束昵,

并將取消操作的具體實現(xiàn)函數(shù) 作為參數(shù)傳遞給 executor ,

executor將其賦值給 cancel 傳遞給用戶

取消操作是執(zhí)行了Promise.resolve

同時將用戶設(shè)定的 message 封裝后作為結(jié)果返回給 then 的實現(xiàn)

其實都是進(jìn)行了promise上的操作流程:

=> source實例化時將取消函數(shù)cancel拋給用戶拔稳,而cancel函數(shù)內(nèi)有一個待定的resolve函數(shù)(resolvePromise)

=> 發(fā)送請求時再將onCanceled定義為cancel函數(shù)內(nèi)的待定resolve函數(shù)

axios源碼 - 發(fā)送請求時

// xhr.js

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

        request.abort();
        reject(cancel);
        // Clean up request
        request = null;
    });
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锹雏,隨后出現(xiàn)的幾起案子巴比,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件轻绞,死亡現(xiàn)場離奇詭異采记,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)政勃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門唧龄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奸远,你說我怎么就攤上這事既棺。” “怎么了懒叛?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵丸冕,是天一觀的道長。 經(jīng)常有香客問我薛窥,道長胖烛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任诅迷,我火速辦了婚禮洪己,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘竟贯。我一直安慰自己,他們只是感情好逝钥,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布屑那。 她就那樣靜靜地躺著,像睡著了一般艘款。 火紅的嫁衣襯著肌膚如雪持际。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天哗咆,我揣著相機(jī)與錄音蜘欲,去河邊找鬼。 笑死晌柬,一個胖子當(dāng)著我的面吹牛姥份,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播年碘,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼澈歉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了屿衅?” 一聲冷哼從身側(cè)響起埃难,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后涡尘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忍弛,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年考抄,在試婚紗的時候發(fā)現(xiàn)自己被綠了细疚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡座泳,死狀恐怖惠昔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情挑势,我是刑警寧澤镇防,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站潮饱,受9級特大地震影響来氧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜香拉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一啦扬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凫碌,春花似錦扑毡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苦掘,卻和暖如春换帜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鹤啡。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工惯驼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人递瑰。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓祟牲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抖部。 傳聞我的和親對象是個殘疾皇子疲眷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

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

  • 實在來不及自己寫了 把讀過的文章先轉(zhuǎn)過來 明天再進(jìn)行編輯 axios項目目錄結(jié)構(gòu) 注:因為我們需要要看的代碼都是...
    vivianXIa閱讀 869評論 0 1
  • axios[https://github.com/axios/axios]是一個基于promise調(diào)用邏輯的htt...
    竹葉寨少主閱讀 1,082評論 0 5
  • 簡介 axios 是一個用于瀏覽器和Node.js上的基于 Promise 的http網(wǎng)絡(luò)庫。 大綱 使用方式安裝...
    _雙眸閱讀 1,035評論 1 1
  • 基類 Axios 跟隨入口 index.js 進(jìn)入/lib/axios.js您朽,第一個方法則是createInsta...
    丶梅邊閱讀 650評論 0 1
  • Vue.js 1.0 我們常使用 vue-resource (官方ajax庫), Vue 2.0 發(fā)布后作者宣告不...
    九四年的風(fēng)閱讀 24,060評論 1 8