The general idea is like this. ajax is encapsulated by promise's chain call, and the request result is returned to axios through the then method. The following is a step-by-step analysis. I hope you can point out the wrong places.
Prepare Axios constructor
Constructor is mainly used to store configuration information. default: it is used to store configuration information passed by axios (as shown in Figure 1). interceptors: intercept requests (as shown in Figure 2). get/post/request: send requests. All get/post here depend on the request method
function Ajax(){ this.default = null; this.interceptors = { request: new InterceptorManger(), response: new InterceptorManger() } } Ajax.prototype.request = function(config){ this.default = config; } Ajax.prototype.get = function(config){ return Ajax.prototype.request(Object.assign({},{method:'get'},config)); } Ajax.prototype.post = function(){ return Ajax.prototype.request(Object.assign({},{method:'post'},config)); }
Prepare a createInstance function. The properties and methods of the function are the same as those of the Ajax instance
Someone here will ask, why prepare a createInstance function? Isn't it good to use Ajax instances directly? If so, it does not conform to the design concept of AXIOS source code. The author wants to send data through AXIOS functions instead of sending requests through AXIOS instances (as shown in Figure 3).
function createInstance(){ var context = new Ajax(); var instance = Ajax.prototype.request.bind(context); instance.CancelToken = CancelToken; Object.keys(context).forEach(function(key){ instance[key] = context[key]; }) Object.keys(Ajax.prototype).forEach(function(key){ instance[key] = Ajax.prototype[key]; }) console.dir(instance); return instance; } var axios = createInstance();
Build the request through the request function
The request axios obtains the requested data through then, so the return value of the request must be a promise object. First, a promise.resolve object needs to be built in the request function, and the configuration file is passed to then through resolve to execute and return ajax results. Here is a note: the return result of promise.then must be a promise object, which is the chain call of promise
Ajax.prototype.request = function(config){ this.default = config; var promise = Promise.resolve(config); var chinas = [dispatchRequest,undefined]; return promise.then(chinas[0],chinas[1]); } function dispatchRequest(config){ return xhrAdapter(config).then(function(response){ return response; },function(error){ console.log(error); }) } // Send ajax request function xhrAdapter(options){ return new Promise(function(resolve,reject){ options = options || {}; options.type = (options.type || "GET").toUpperCase(); options.dataType = options.dataType || "json"; var params = formatParams(options.data); if (window.XMLHttpRequest) { var xhr = new XMLHttpRequest(); } else { //IE6 and below browsers var xhr = new ActiveXObject('Microsoft.XMLHTTP'); } if (options.type == "GET") { xhr.open("GET", options.url + "?" + params, true); xhr.send(null); } else if (options.type == "POST") { xhr.open("POST", options.url, true); //Set the content type when the form is submitted xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send(params); } xhr.onreadystatechange = function () { if (xhr.readyState == 4) { var status = xhr.status; if (status >= 200 && status < 300) { resolve({ config: options, data: xhr.response, headers: xhr.getAllResponseHeaders(), request: xhr, status: status, statusText: xhr.statusText }); } else { reject(new Error("Request failed, request status code:"+status)); } } } if(options.cancelToken){ options.cancelToken.promise.then(function(){ xhr.abort(); }) } }) } //Format parameters function formatParams(data) { var arr = []; for (var name in data) { arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name])); } arr.push(("v=" + Math.random()).replace(".","")); return arr.join("&"); }
Prepare InterceptorManger constructor to request interception
Now our main functions have been realized. Now we mainly complete some auxiliary functions, which is the characteristic part of axios. First of all, we need to understand that request interception is performed before sending the request, response interception is performed after sending the request, and some modifications need to be made in the request function, Store the callback function of request interception success / failure in the China array, and store the callback function of response interception success / failure in the China array before sending the request, and execute it successively after sending the request
Ajax.prototype.request = function(config){ this.default = config; var promise = Promise.resolve(config); var chinas = [dispatchRequest,undefined]; this.interceptors.request.handle.forEach(function(item){ chinas.unshift(item.rejected); chinas.unshift(item.resolved); }) this.interceptors.response.handle.forEach(function(item){ chinas.push(item.resolved); chinas.push(item.rejected); }) while(chinas.length>0){ promise = promise.then(chinas.shift(),chinas.shift()); } return promise; } function InterceptorManger(){ this.handle = []; } InterceptorManger.prototype.use = function(resolved,rejected){ this.handle.push({resolved,rejected}) }
Prepare the CancelToken constructor to cancel the request
Execute the cancel function through axios (as shown in Figure 4). The cancel function is the promise.resolve method instantiated by CancelToken. The execution of this method will call the promise.then method to cancel the request in ajax, which is equivalent to a process of subscribing to the publication and delaying the processing of the cancel function
function xhrAdapter(options){ return new Promise(function(resolve,reject){ ...... if(options.cancelToken){ options.cancelToken.promise.then(function(){ xhr.abort(); }) } }) } function CancelToken(executor){ var resultResolve = null; this.promise = new Promise(function(resolve){ resultResolve = resolve }) executor(resultResolve); }