//暗号:axios
处理异步在处理前后端的业务时的考虑问题,前端的Ajax = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)就是处理异步的问题,随后Fetch和Axios进一步封装了Ajax,使得接口更加丰富。
1. Axios的创建
axios({
url,
method,
headers,
})
axios(url, {
method,
headers,
})
axios.get(url, {
headers,
})
axios.post(url, data, {
headers,
})
axios.create(optios)
axios创建方法方法: 1. axios() -- axios方法创建, 2. axios.create()对象的create方法创建, 3.axios.request()
// Axios引入 (axios/index.js) module.exports = require(‘./lib/axios‘)
axios/lib/axios.js中的createInstance能够实现axios的多种创建方式
function createInstance(defaultConfig) {
// 创建一个Axios的实例但是最终返回的并不是这个实例 var context = new Axios(defaultConfig);
// 创建一个instance的容器,指向Axios.prototyep.request方法,并上下文指向context,最终返回。
// 我们能用Axios() => instance() var instance = bind(Axios.prototype.request, context); // 把Axios.prototype上的方法扩展到instance对象上。
// 这样instance又有了get、post、put等方法
// 并指向上下文context,这样执行Axios原型链上的方法时,this会指向context utils.extend(instance, Axios.prototype, context); // 把context对象上的自身属性和方法扩展到instance上
// extend方法会遍历对象本身,不会表里原型链上的方法和属性
// instance就有了create,default, interceptors的方法和属性
utils.extend(instance, context); return instance; } // 调用axios()方法时候,使用默认配置 var axios = createInstance(defaults); // Expose Axios class to allow class inheritance axios.Axios = Axios; // Factory for creating new instances
// 合并配置项
axios.create = function create(instanceConfig) { return createInstance(mergeConfig(axios.defaults, instanceConfig)); };
Axios模块是axios的核心,一个Axios实例对应一个axios对象,其他方法都是对Axios内容的扩展
Axios构造函数的核心方法是request方法,各种axios的调用方式最终都是通过request方法发请求的。
Axios构造函数的核心方法是request方法,各种axios的调用方式最终都是通过request方法发请求的。
调用axios等同于Axios.prototype.request
调用axios.reuqest等同于Axios.prototype.request
调用axios.get等同于Axios.ptototype.get
// /lib/core/Axios.js
function Axios(instanceConfig) { this.defaults = instanceConfig; this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager() }; } Axios.prototype.request = function request(config) { // ...处理配置和参数合并处理,省略代码 }; // 为支持的请求方法提供别名 utils.forEach([‘delete‘, ‘get‘, ‘head‘, ‘options‘], function forEachMethodNoData(method) { Axios.prototype[method] = function(url, config) { return this.request(utils.merge(config || {}, { method: method, url: url })); }; }); utils.forEach([‘post‘, ‘put‘, ‘patch‘], function forEachMethodWithData(method) { Axios.prototype[method] = function(url, data, config) { return this.request(utils.merge(config || {}, { method: method, url: url, data: data })); }; });
2. 统一发送请求的适配器adapter
adapter是xhr.js封装的promise对象, 它统一了不同环境发送请求的需求。
// default.js
// 适配器
var adapter = config.adapter || defaults.adapter; return adapter(config).then(function onAdapterResolution(response) { throwIfCancellationRequested(config); // 处理不同环境发出的request请求 function getDefaultAdapter() { var adapter; if (typeof XMLHttpRequest !== ‘undefined‘) { // 处理浏览器的发出的请求 adapter = require(‘./adapters/xhr‘);
} else if (typeof process !== ‘undefined‘ && Object.prototype.toString.call(process) === ‘[object process]‘) { // For node use HTTP adapter // 处理node环境的发出的请求 adapter = require(‘./adapters/http‘); } return adapter; }
// xhr.js // 返回xhr的promise对象 module.exports = function xhrAdapter(config) { return new Promise(function dispatchXhrRequest(resolve, reject) { var request = new XMLHttpRequest(); // Send the request request.send(requestData); }); };
3. InterceptoManager 拦截器
Axios.js中有一个拦截器InterceptorManager, 可以简单理解为一个request或者response的数组,每次从队列头部或者尾部传入request或者response的promise对象fullfilled和rejected函数
// InterceptorManager.js // InterceptorManager方法维护一个数组 function InterceptorManager() { this.handlers = []; } // 每次在数组尾部加入promise对象, use方法进行注册 InterceptorManager.prototype.use = function use(fulfilled, rejected) { this.handlers.push({ fulfilled: fulfilled, rejected: rejected }); return this.handlers.length - 1; };
4. 执行链处理拦截器的请求和响应
处理ajax的promise请求前,执行链chain会把拦截器数组中的promise对象请求unshift和响应push加入chain数组中,
// 创建一个执行链 [dispatchRequest, undefined], dispatchRequest是适配器ajax中封装的promise请求 var chain = [dispatchRequest, undefined]; var promise = Promise.resolve(config); this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { chain.unshift(interceptor.fulfilled, interceptor.rejected); }); this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { chain.push(interceptor.fulfilled, interceptor.rejected); });
// 执行顺序promise.then(rs2, rf2).then(rs1, rf1). then(dispatchRequest, undefine).then(ps1, pf1).then(ps2, pf2)... while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise; };
5. 执行链处理请求和响应的promise对象的顺序
数组的 shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
每次执行while循环,从chain数组里按序取出两项,并分别作为promise.then方法的第一个和第二个参数
按照我们使用InterceptorManager.prototype.use添加拦截器的规则,正好每次添加的就是我们通过InterceptorManager.prototype.use方法添加的成功和失败回调
通过InterceptorManager.prototype.use往拦截器数组里添加拦截器时使用的数组的push方法,
对于请求拦截器,从拦截器数组按序读到后是通过unshift方法往chain数组数里添加的,又通过shift方法从chain数组里取出的,所以得出结论:对于请求拦截器,先添加的拦截器会后执行
对于响应拦截器,从拦截器数组按序读到后是通过push方法往chain数组里添加的,又通过shift方法从chain数组里取出的,所以得出结论:对于响应拦截器,添加的拦截器先执行
第一个请求拦截器的fulfilled函数会接收到promise对象初始化时传入的config对象,而请求拦截器又规定用户写的fulfilled函数必须返回一个config对象,所以通过promise实现链式调用时,每个请求拦截器的fulfilled函数都会接收到一个config对象
第一个响应拦截器的fulfilled函数会接受到dispatchRequest(也就是我们的请求方法)请求到的数据(也就是response对象),而响应拦截器又规定用户写的fulfilled函数必须返回一个response对象,所以通过promise实现链式调用时,每个响应拦截器的fulfilled函数都会接收到一个response对象
任何一个拦截器的抛出的错误,都会被下一个拦截器的rejected函数收到,所以dispatchRequest抛出的错误才会被响应拦截器接收到。
因为axios是通过promise实现的链式调用,所以我们可以在拦截器里进行异步操作,而拦截器的执行顺序还是会按照我们上面说的顺序执行,也就是 dispatchRequest 方法一定会等待所有的请求拦截器执行完后再开始执行,响应拦截器一定会等待 dispatchRequest 执行完后再开始执行。
// 暗号:axios