Axios是一个很常用的http请求库,功能强大,应用也很灵活,可用于node和浏览器环境。也可以扩展自己的请求方法。我们可以通过调试源码来更深入的理解底层原理,学习其思想。
github地址:axiso
可以在线类似vscode访问,在github后面加上1s即可:
https://github1s.com/axios/axios
axios对象
axios对象是通过调用createInstance创建出来的。
经过如下处理axios既是个方法,又含有内部request方法。
function Axios() {}
Axios.prototype.request = function(msg) {
console.log('Send msg:' + msg)
}
var context = new Axios();
var axios = Axios.prototype.request.bind(context)
axios.request = Axios.prototype.request
// below equals
axios("hello")
axios.request('hello1')
底下是具体的源码实现。
'use strict';
var utils = require('./utils');
var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var mergeConfig = require('./core/mergeConfig');
var defaults = require('./defaults');
/**
* Create an instance of Axios
*
* @param {Object} defaultConfig The default config for the instance
* @return {Axios} A new instance of Axios
*/
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context);
// Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context);
// Copy context to instance
utils.extend(instance, context);
// Factory for creating new instances
instance.create = function create(instanceConfig) {
return createInstance(mergeConfig(defaultConfig, instanceConfig));
};
return instance;
}
// Create the default instance to be exported
var axios = createInstance(defaults);
// Expose Axios class to allow class inheritance
axios.Axios = Axios;
// Expose Cancel & CancelToken
axios.CanceledError = require('./cancel/CanceledError');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
axios.VERSION = require('./env/data').version;
axios.toFormData = require('./helpers/toFormData');
// Expose AxiosError class
axios.AxiosError = require('../lib/core/AxiosError');
// alias for CanceledError for backward compatibility
axios.Cancel = axios.CanceledError;
// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = require('./helpers/spread');
// Expose isAxiosError
axios.isAxiosError = require('./helpers/isAxiosError');
module.exports = axios;
// Allow use of default import syntax in TypeScript
module.exports.default = axios;
拦截器的设计
在new Axios对象的时候就创建一个interceptors对象,里面包括一个request和response。
InterceptorManager定义了一个原形方法,use方法,那么此时我们就可以通过下面的:
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
function InterceptorManager() {
this.handlers = [];
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected,
synchronous: options ? options.synchronous : false,
runWhen: options ? options.runWhen : null
});
return this.handlers.length - 1;
};
那么拦截器在何时调用呢?当然是在我们发request的时候调用。
这里他使用了一个chain数组来存放我们的拦截器,具体有以下几步:
1. chain数组默认放了两个值[dispatchRequest, undefined],dispatchRequest是我们实际发请求的
2. this.interceptors.request.handlers.forEach(i=>chain.unshift(i.fullfilled, i.rejected))
此时把request的拦截器放在了数组的头部(unshift操作)。
3. this.interceptors.response.handlers.forEach(i=>chain.push(i.fulfilled, i.rejected))
此时把response的拦截器放在了数组的尾部(push操作)。
假如request和response拦截器各有两个,最终形成的数组如下:
[
interceptors.request.handlers[1].fulfilled,
interceptors.request.handlers[1].rejected,
interceptors.request.handlers[0].fulfilled,
interceptors.request.handlers[0].rejected,
dispatchRequest,
undefined,
interceptors.response.handlers[0].fulfilled,
interceptors.response.handlers[0].rejected,
interceptors.response.handlers[1].fulfilled,
interceptors.response.handlers[1].rejected,
]
此时就可以使用promise的then来进行顺序调用了,先执行request拦截器,再执行sendRequest,再执行response拦截器,每次执行结果都是一个promise,形成链式关系。
promise = Promise.resolve(config);// config将作为request拦截器fullfilled的参数
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
Axios.prototype.request = function request(configOrUrl, config) {
/*eslint no-param-reassign:0*/
// Allow for axios('example/url'[, config]) a la fetch API
if (typeof configOrUrl === 'string') {
config = config || {};
config.url = configOrUrl;
} else {
config = configOrUrl || {};
}
config = mergeConfig(this.defaults, config);
// Set config.method
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get';
}
var transitional = config.transitional;
if (transitional !== undefined) {
validator.assertOptions(transitional, {
silentJSONParsing: validators.transitional(validators.boolean),
forcedJSONParsing: validators.transitional(validators.boolean),
clarifyTimeoutError: validators.transitional(validators.boolean)
}, false);
}
// filter out skipped interceptors
var requestInterceptorChain = [];
var synchronousRequestInterceptors = true;
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
return;
}
synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});
var responseInterceptorChain = [];
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});
var promise;
if (!synchronousRequestInterceptors) {
var chain = [dispatchRequest, undefined];
Array.prototype.unshift.apply(chain, requestInterceptorChain);
chain = chain.concat(responseInterceptorChain);
promise = Promise.resolve(config);
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
}
var newConfig = config;
while (requestInterceptorChain.length) {
var onFulfilled = requestInterceptorChain.shift();
var onRejected = requestInterceptorChain.shift();
try {
newConfig = onFulfilled(newConfig);
} catch (error) {
onRejected(error);
break;
}
}
try {
promise = dispatchRequest(newConfig);
} catch (error) {
return Promise.reject(error);
}
while (responseInterceptorChain.length) {
promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());
}
return promise;
};
cancelToken的设计
我们在request的config参数会加入如下信息
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
})
});
cancel(); // 取消请求
来看看newCancelToken干了啥?
上面的cancel=c中的c就是下面加粗部分的代码,也就是说当我们调用cancel()也就是下面加粗部分会被调用,可以看到它竟然resolve了一个Promise。有resolve得有then啊,那then在哪呢?
继续往下分析:
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;
// eslint-disable-next-line func-names
this.promise.then(function(cancel) {
if (!token._listeners) return;
var i;
var l = token._listeners.length;
for (i = 0; i < l; i++) {
token._listeners[i](cancel);
}
token._listeners = null;
});
// eslint-disable-next-line func-names
this.promise.then = function(onfulfilled) {
var _resolve;
// eslint-disable-next-line func-names
var promise = new Promise(function(resolve) {
token.subscribe(resolve);
_resolve = resolve;
}).then(onfulfilled);
promise.cancel = function reject() {
token.unsubscribe(_resolve);
};
return promise;
};
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new CanceledError(message);
resolvePromise(token.reason);
});
}
/**
* Throws a `CanceledError` if cancellation has been requested.
*/
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
throw this.reason;
}
};
/**
* Subscribe to the cancel signal
*/
CancelToken.prototype.subscribe = function subscribe(listener) {
if (this.reason) {
listener(this.reason);
return;
}
if (this._listeners) {
this._listeners.push(listener);
} else {
this._listeners = [listener];
}
};
/**
* Unsubscribe from the cancel signal
*/
CancelToken.prototype.unsubscribe = function unsubscribe(listener) {
if (!this._listeners) {
return;
}
var index = this._listeners.indexOf(listener);
if (index !== -1) {
this._listeners.splice(index, 1);
}
};
我们去dispatchRequest里面看看,因为我们想取消发送只能在此处进行。可以看到发请求时会调用一个叫做throwIfCancellationRequested的方法,它会调用我们在cancelToken里面定义的一个原形方法throwIfRequested。
可以看到throwIfCancellationRequested在三个地方调用了,
- 在发请求之前判断
- 在服务器返回之后判断
- 在请求出问题的时候判断
function dispatchRequest(config) {
throwIfCancellationRequested(config);
// Ensure headers exist
config.headers = config.headers || {};
// Transform request data
config.data = transformData.call(
config,
config.data,
config.headers,
config.transformRequest
);
// Flatten headers
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers
);
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
// Transform response data
response.data = transformData.call(
config,
response.data,
response.headers,
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData.call(
config,
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
};
在看一下xhr.js, 会通过subscribe去订阅onCanceled事件,如果有人cancel,就会调用abort()方法终止请求
if (config.cancelToken || config.signal) {
// Handle cancellation
// eslint-disable-next-line func-names
onCanceled = function(cancel) {
if (!request) {
return;
}
reject(!cancel || (cancel && cancel.type) ? new CanceledError() : cancel);
request.abort();
request = null;
};
config.cancelToken && config.cancelToken.subscribe(onCanceled);
if (config.signal) {
config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
}
}
发表评论
所有评论(0)