使用者 - 導(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;
});
}