傳送門:
- axios源碼簡析(一)——axios入口文件
- axios源碼簡析(二)——Axios類與攔截器
- axios源碼簡析(三)——請求與取消請求
請求過程
在Axios.prototype.request
中我們看到,要先通過請求攔截器,才能進行請求。下面看一下dispatchRequest()
是如何實現的
// /lib/core/dispatchRequest.jsmodule.exports = function dispatchRequest(config) {// 判斷是否已經取消請求throwIfCancellationRequested(config);/* 對請求的url、headers、data進行處理 */// 發動請求的函數,返回一個promisevar adapter = config.adapter || defaults.adapter;return adapter(config).then(function onAdapterResolution(response) {// 判斷是否已經取消請求throwIfCancellationRequested(config);// 處理返回的數據response.data = transformData(response.data,response.headers,config.transformResponse);return response;}, function onAdapterRejection(reason) {if (!isCancel(reason)) {// 判斷是否已經取消請求throwIfCancellationRequested(config);// 處理返回的錯誤信息if (reason && reason.response) {reason.response.data = transformData(reason.response.data,reason.response.headers,config.transformResponse);}}return Promise.reject(reason);});
如果用戶有在配置中傳入adapter
,將使用defaults.adapter
,根據運行環境是瀏覽器還是nodejs采取不同的請求方式。
// /lib/defaults.js
function getDefaultAdapter() {var adapter;if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {// nodejs環境adapter = require('./adapters/http');} else if (typeof XMLHttpRequest !== 'undefined') {// 瀏覽器環境adapter = require('./adapters/xhr');}return adapter;
}var defaults = {adapter: getDefaultAdapter(),/* 其他配置 */
};modules.exports = defaults;
/lib/adapters/http.js
與/lib/adapters/xhr.js
兩個文件導出的函數都返回一個promise,具體的實現方式就不分析了。里面有很多http請求的細節,可以仔細研究。
取消請求
官方文檔中的調用方法
const CancelToken = axios.CancelToken;
const source = CancelToken.source();axios.get('/user/12345', {cancelToken: source.token
}).catch(function(thrown) {if (axios.isCancel(thrown)) {console.log('Request canceled', thrown.message);} else {// handle error}
});axios.post('/user/12345', {name: 'new name'
}, {cancelToken: source.token
})// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
我們進入CancelToken
類,找到了CancelToken.source()
方法:
// /lib/cancel/CancelTokenCancelToken.source = function source() {var cancel;var token = new CancelToken(function executor(c) {cancel = c;});return {token: token,cancel: cancel};
};
可以看出,CancelToken.source().token
是一個CancelToken
類的實例,CancelToken.source().cancel
是new CacelToken()
時傳入參數(一個函數)的參數(也是個函數),通過CancelToken
的構造函數可以看出:
// /lib/cancel/CancelTokenfunction 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 requestedreturn;}token.reason = new Cancel(message);resolvePromise(token.reason);});
}
CancelToken.source().cancel
就是這個函數:
function cancel(message) {if (token.reason) {// Cancellation has already been requestedreturn;}token.reason = new Cancel(message);resolvePromise(token.reason);
}
CancelToken.source().token
有promise
和reason
兩個屬性,promise
一直處于 pending
狀態,reason
屬性是一個Cancel
類的實例,Cancel
類的構造函數如下:
// /lib/cancel/Cancel.js
function Cancel(message) {this.message = message;
}Cancel.prototype.toString = function toString() {return 'Cancel' + (this.message ? ': ' + this.message : '');
};Cancel.prototype.__CANCEL__ = true;
在源碼中,有以下幾種方式檢測是否執行了取消請求。
1 檢測config.cancelToken
是否有reason
屬性,如果有,將reason
拋出,axios
進入rejected
狀態。
// /lib/core/dispatchRequest.js
function throwIfCancellationRequested(config) {if (config.cancelToken) {config.cancelToken.throwIfRequested();}
}module.exports = function dispatchRequest(config) {// 判斷是否已經取消請求throwIfCancellationRequested(config);/* ... */
};// /lib/cancel/CancelToken
CancelToken.prototype.throwIfRequested = function throwIfRequested() {if (this.reason) {throw this.reason;}
};
2 在請求過程中,執行CancelToken.source().token
的promise
屬性中的resolve
函數,參數是CancelToken.source().token.reason
,并將其拋出,promise進入rejected
狀態
if (config.cancelToken) {// Handle cancellationconfig.cancelToken.promise.then(function onCanceled(cancel) {if (!request) {return;}// 取消請求request.abort();// promise進入rejectedreject(cancel);// Clean up requestrequest = null;});
}
調用方法中catch
接到的thrown
,就是CancelToken.source().token.reason
。
如果在使用axios時候,只在config
中添加{cancelToken: source.token}
,而不調用source.cancel()
,則CancelToken.source().token
不會有reason
屬性,CancelToken.source().token.promise
也一直是pending
狀態。請求不會取消。
參考
深入淺出 axios 源碼
axios源碼分析——取消請求