Take a look at the whole Axios flow chart!!

Definition of Axios methods

function Axios(instanceConfig) {
  // Accept the default configuration parameters
  this.defaults = instanceConfig;
  // Define request and response interceptors
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}
Copy the code

When we create the Axios object, we pass in a default configuration object, assign it to internal properties and then merge it with our own configured parameters. Request and response interceptor objects are then defined, and users can manually add interceptor functions.

Axios.prototype.request = function request(config) {
  // This is preprocessing the arguments, because Axios passes them in several ways
  if (typeof config === 'string') {
    config = arguments[1) | | {}; config.url =arguments[0];
  } else {
    config = config || {};
  }
  // Merge our configured parameters with the original parameters
  config = mergeConfig(this.defaults, config);

  // Set config.method
  if (thod) {
    config.methconfig.meod = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }

  // The chain array is used to store functions that handle config or result. These functions come in pairs. By default, dispatchRequest and undefine are passed in.
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);
// Put the user-added request blocker into the chain, in this case the queue header, because it is to be executed before the request is initiated.
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
// Put the user-added response-interceptor function into the chain, at the end of the queue, because this is to process the returned data.
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
  // Loop through, and the promise gets the final data
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};
Copy the code

The request method in Axios does three things:

  • Merges the configuration parameters sent by the user
  • Adds user-configured request and response interception to the execution queue
  • Execute request interception function —-> Initiate request —–> execute response interception function
// Add the ['delete', 'get', 'head', 'options'] methods to the Axios prototype
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
    }));
  };
});
// Add the ['post', 'put', 'patch'] methods to the Axios prototype
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
    }));
  };
});
Copy the code

Get (‘ XXX ‘, {}). The request method will be called directly from the request method on the Axios object. The request method will be called from the Axios object.

Creation of axios objects

function createInstance(defaultConfig) {
  // Create an axios object passing in the default configuration parameters
  var context = new Axios(defaultConfig);
  // Recreate a function using the bind method and copy the methods and properties from the Axios prototype to the function
  var instance = bind(Axios.prototype.request, context);
  utils.extend(instance, Axios.prototype, context);
  utils.extend(instance, context);
  return instance;
}
// Create the request method. This object is the method we use to finally send the request. Note that Axios is a function.
var axios = createInstance(defaults);
Copy the code

The createInstance function creates a function, and we can call the function or the prototype request method to send a request.

  • Create an AXIos object passing in our default configuration parameters

  • Call bind and create a new function that returns the value of axios. request.

    function bind(fn, thisArg){
    	return function wrap(. args){
    		returnfn.apply(thisArg,args); }}Copy the code

    Args is the parameter that we configure when we send the request. Aegs eventually passes the request to axios.request. The reason we can send the request using axios.get() is because we copied the method from the Axios prototype to the wrap function, which is extend above.

    axios.Cancel = require('./cancel/Cancel');
    axios.CancelToken = require('./cancel/CancelToken');
    axios.isCancel = require('./cancel/isCancel');
    Copy the code

    This piece is to add the cancel request method to Axios, allowing us to interrupt the sent request, which we’ll see later.

Call dispatchRequest to initiate a request

  config.data = transformData(
    config.data,
    config.headers,
    config.transformRequest
  );
Copy the code

First is to we transform the incoming parameters, axios. Defaults. TransformRequest default has a function in the array, he will according to our parameters of different types of conversion, For example, if **{“name”:”zhangsan”,”age”:12}** is passed as an argument, the result is as follows:

It will be converted to a JSON string. If we want to customize the conversion function we can concat the custom function.

axios.get('xxxx', {
  transformResponse: axios.defaults.transformResponse.concat(function (data, headers) {
    //....
    returndata; })})Copy the code

TransformData execution time will traverse axios. Defaults. The conversion function in transformRequest array data transformation, the next is to send the request.

return adapter(config).then(function onAdapterResolution(response) {
    throwIfCancellationRequested(config);
    / /... Process response
    return response;
  }, function onAdapterRejection(reason) {
    if(! isCancel(reason)) { throwIfCancellationRequested(config);// Transform response data
      if (reason && reason.response) {
        / /... Handle reason}}return Promise.reject(reason);
  });
Copy the code

The key here is to call the Adapter function to send the request and then process the data that is returned. In other words, the dispatchRequest function returns a Promise, and we can get the data by calling its then method.

Adapter method

First let’s trace back to how the adapter called in dispatchRequest was retrieved.

var adapter = config.adapter || defaults.adapter;
Copy the code

The adapter is inherited from the default config. The defaults.adapter is derived from the default config.

var defaults = {
	adapter: getDefaultAdapter(), 
    / /...
}

function getDefaultAdapter() {
  var adapter;
  // Use XMLHttpRequest to determine whether the browser environment is current. If not, use process to determine whether the node environment is present
  if (typeofXMLHttpRequest ! = ='undefined') {
    adapter = require('./adapters/xhr');
  } else if (typeofprocess ! = ='undefined' && Object.prototype.toString.call(process) === '[object process]') {
    adapter = require('./adapters/http');
  }
  return adapter;
}
Copy the code

As you can see from getDefaultAdapter, AXIOS supports both the browser and Node environments, including XMLHttpRequest in the browser environment and HTTP in the Node environment. Let’s focus on the browser environment.

module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    / /... The data processing
    var request = new XMLHttpRequest();
	/ /...
    request.open();
    // Set the timeout period
    request.timeout = config.timeout;
    request.onreadystatechange = function handleLoad() {
      / /... Process the returned data
      settle(resolve, reject, response);
      request = null;
    };
    // Listen for interrupt operations
    request.onabort = function handleAbort() {};
    // Listening error
    request.onerror = function handleError() {};
    // Listen timed out
    request.ontimeout = function handleTimeout() {};
    if (requestData === undefined) {
      requestData = null;
    }
    request.send(requestData);
  });
};
Copy the code

This is an overview of the XML request process, from which you can see the error handling, request interception, interrupt requests, request timeout handling, and other functions implemented in AXIOS.

WithCredentials:

 if(! utils.isUndefined(config.withCredentials)) { request.withCredentials = !! config.withCredentials; }Copy the code

Those of you who are familiar with CORS should be familiar with withCredentials. This attribute is a Boolean value that sets whether or not authorization information, such as cookies or authorization headers, is carried when making cross-domain requests.

Monitor upload and download progress:

// Monitor the download progress
if (typeof config.onDownloadProgress === 'function') {
      request.addEventListener('progress', config.onDownloadProgress);
}
// Monitor the upload progress
if (typeof config.onUploadProgress === 'function' && request.upload) {
      request.upload.addEventListener('progress', config.onUploadProgress);
}
Copy the code

Progress is an event that comes with XML. When the request receives more data, it will be triggered periodically. When we define config, we can customize the upload or download event, and then we can know the upload or download progress through the event object.

axios.post(action, formData, {
   onUploadProgress: (e) = > {
       // Get the current upper volume and total volume
       let percentage = Math.round((e.loaded * 100) / e.total) || 0;
       if(percentage < 100) {
            / /... Progress bar Displays the current progress}}})Copy the code

Interrupt request:

Let’s start with an example of how to interrupt a request:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('xxxxx', {
  cancelToken: source.token
}).then((res) = > {
    console.log(res)
}).catch((err) = > {
  console.log(err);
});
source.cancel('Interrupt request');

Copy the code

As you can see from the code, we first need to configure the cancelToken attribute on our request. Then we can call the cancel method to interrupt the request at any time before it returns.

Let’s break down the process step by step.

  • Get CancelToken

    axios.CancelToken = require('./cancel/CancelToken');
    Copy the code

    Now we know that the CancelToken on Axios is derived from the./cancel/CancelToken module.

    function CancelToken(executor) {
      var resolvePromise;
      this.promise = new Promise(function promiseExecutor(resolve) {
        resolvePromise = resolve;
      });
      var token = this;
      executor(function cancel(message) {
        if (token.reason) {
          return;
        }
        token.reason = new Cancel(message);
        resolvePromise(token.reason);
      });
    }
    Copy the code

    CancelToken is a method that binds a promise to this. Based on past experience, this refers to either the CancelToken instance or a new object from new.

    We then execute the executor function, which passes a cancel method, which is actually the user’s cancel method.

    In the cancel method, we see that it calls the Promise’s resolve method to throw the message, so we can look at the flow chart above.

  • Perform CancelToken. Source

    CancelToken.source = function source() {
      var cancel;
      var token = new CancelToken(function executor(c) {
        cancel = c;
      });
      return {
        token: token,
        cancel: cancel
      };
    };
    Copy the code

    The source method first creates a new instance of the token, and now we know that this refers to the new instance. The token has a pending promise waiting for the user to trigger the cancel.

    This function returns a token, which is the value of the CancelToken attribute of config, and a cancel function, which is the argument passed by the Executor function in CancelToken.

  • Cancel function

    We now know that the purpose of cancel is to change the state of the promise in CancelToken from Pendinig to resolve. What does this do? Since the token we generate will eventually be passed in as the value of the CancelToken attribute, let’s see how the CancelToken attribute is ultimately handled.

    if (config.cancelToken) {
          config.cancelToken.promise.then(function onCanceled(cancel) {
            if(! request) {return;
            }
    
            request.abort();
            reject(cancel);
            request = null;
          });
    }
    Copy the code

    It should now become clear that this code will be executed when executing the XML request. If CancelToken is configured, it will enter the judgment condition, which will call canceltoken.promise.then, If the user does not call cancel to change the canceltoken. promise state from pending to resolve, his then method will not be executed and will not affect the normal request.

    If we call cancel, then we execute the code in the THEN method, and we see that it fires ABORT in THE XML, reject, and we know that axios returns a promise, The reject corresponds to this promise.

conclusion

Axios is a very powerful library that you can use on a daily basis, but it’s also worth learning a little bit about the call logic, such as the final cancel logic, which gives the user control over the promise that Axios returns, and triggers his reject method when he wants to terminate.