The original link

Introduce axios

I have been using the AXIos library, and while enjoying the convenience it brings, I always feel a little sorry for not reading its source code. There are few articles about axios source code on the Internet, so I write this article as an introduction.

Axios is a homogeneous asynchronous JavaScript request library that can be used on the browser side and in NodeJS environments.

The author of VueJS also recommends this tool, which has the following functions in addition to the asynchronous request for network resources:

  1. Provide proxy function
  2. Interceptors (middleware like) are provided to register actions before the request is sent and after the response is received
  3. You can obtain the upload and download progress
  4. Adapter options are provided to simulate response data
  5. Customize the response code range that causes an error
  6. Provides the ability to cancel requests

GitHub address for Axios.

So how does it work?

First, why can it be used on the browser side and in NodeJS environments

In AXIos, the adapter design pattern is used to mask platform differences, allowing consumers to make HTTP requests using the same set of apis on the browser side and in the NodeJS environment.

The adapter in axios’s default configuration is obtained using the getDefaultAdapter() method, which has the following logic:

function getDefaultAdapter() {
  var adapter;
  // Only Node.JS has a process variable that is of [[Class]] process
  if (typeofprocess ! = ='undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  } else if (typeofXMLHttpRequest ! = ='undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  }
  return adapter;
}
Copy the code

As shown in the above code, different apis are selected to initiate HTTP requests based on the characteristics of the runtime environment.

Next, I’ll look at these two files, HTTP and XHR, respectively.

http.js

This file, which references NodeJS ‘HTTP and HTTPS libraries, is used to make HTTP requests and receive the results using Promise.

We all know that when making an HTTP request, the most important thing is to comply with the HTTP protocol and write the correct request header. Axios receives the user’s custom parameters, including the request header, request parameters, and so on, by passing in config. An HTTP request is then initiated internally using (HTTP/HTTPS).request(options, callback).

For details on how to integrate and handle the incoming parameters, please download the source code to see.

xhr.js

Http-like logic, except that the XMLHTTPRequest interface of WebAPI is invoked to initiate an HTTP request.

Implementation of interceptors

Axios provides the interceptor capability to process the incoming Config or other operations before the request is initiated, or to process the response after the response is received.

We can look at the Axios constructor, which is very simple:

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}
Copy the code

The InterceptorManager maintains an array to collect interceptor functions, including fulfilled and Rejected, which correspond to the Promise’s onSuccess and onFail callbacks respectively. Next, see how the interceptor and the HTTP request are combined. Let’s look at the request method on the Axios prototype:

Axios.prototype.request = function request(config) {
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  if (typeof config === 'string') {
    config = arguments[1) | | {}; config.url =arguments[0];
  } else {
    config = config || {};
  }

  config = mergeConfig(this.defaults, config);
  config.method = config.method ? config.method.toLowerCase() : 'get';

  // Hook up interceptors middleware
  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);
  });

  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};
Copy the code

As you can see from the above, they combine the interceptor with the action that initiates the HTTP request using a Promise. The interceptors.request is scheduled before the action that initiates the HTTP request. Interceptors. Response is scheduled after the HTTP request is initiated.

Upload and download progress

Axios provides the ability to observe upload and download progress, but only in a browser environment. The core code is as follows:

// Handle progress if needed
if (typeof config.onDownloadProgress === 'function') {
  request.addEventListener('progress', config.onDownloadProgress);
}

// Not all browsers support upload events
if (typeof config.onUploadProgress === 'function' && request.upload) {
  request.upload.addEventListener('progress', config.onUploadProgress);
}
Copy the code

The upload progress callback is the progress event of the upload property of the XMLHTTPRequest object. The upload progress callback is the progress event of the upload property of the XMLHTTPRequest object.

Simulated response data

The official documentation states that this feature requires the developer to return a Promise object and return a valid Response object in the Promise:

// `adapter` allows custom handling of requests which makes testing easier.
// Return a promise and supply a valid response (see lib/adapters/README.md).
adapter: function (config) {
  / *... * /
}
Copy the code

We can find the implementation of this function in the source code:

var adapter = config.adapter || defaults.adapter;

return adapter(config).then(function onAdapterResolution(response) {
  throwIfCancellationRequested(config);

  // Transform response data
  response.data = transformData(
    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( reason.response.data, reason.response.headers, config.transformResponse ); }}return Promise.reject(reason);
});
Copy the code

As you can see from the above, if we make an HTTP request using AXIos and the config object passed in has an Adapter attribute, this attribute replaces the default adapter (NodeJS http.request() or XMLHTTPRequest). So we need to return a Promise in the Adapter property of config, and this Promise will return a valid Response object.

Customize the response code range that causes an error

Axios provides a feature to customize the range of error response codes, which can be configured via config.validatestatus.

The default range is between 200 and 300:

validateStatus: function validateStatus(status) {
  return status >= 200 && status < 300;
}
Copy the code

In the source code, this method is called through lib\core\settle. Js:

module.exports = function settle(resolve, reject, response) {
  var validateStatus = response.config.validateStatus;
  if(! validateStatus || validateStatus(response.status)) { resolve(response); }else {
    reject(createError(
      'Request failed with status code ' + response.status,
      response.config,
      null, response.request, response )); }};Copy the code

As you can see from above, the input arguments to Settle are similar to resolve and Reject of Promise, so let’s see where Settle is called again.

Sure enough, settle can be found in lib\ Adapters \ HTTP. js and lib\ Adapters \xhr.js.

Without going into the details, axios passes resolve and reject back into settle when it makes an HTTP request using a Promise. Let it decide whether the Promise state is onResolved or onRejected.

Cancel the requested function

The official Axios documentation states that Axios provides the ability to cancel requests that have already been sent.

The axios cancel token API is based on the withdrawn cancelable promises proposal.

The quote above indicates that this was a promise offer, which has since been withdrawn.

Instead of relying on this proposal, we could write a simple cancel request, as long as you’re familiar with closures.

We can use closures to maintain a state of cancellation and then reject the Promise onResolved callback if it needs to be cancelled.

function dispatchRequest(config) {
  let hasCancled = false;
  return Promise((resolve, reject) = > {
    if (hasCancled) {
      reject({ hasCancled: true})}else {
      /** Handle normal response **/
    }
  })
    .then(/** Other business operations **/)
    .catch(err= > {
      if (err.hasCancled) {
        /** The processing request was cancelled */}})}Copy the code

conclusion

Finally, we can get a sense of what makes AXIos powerful: the adapter pattern shields platform variability, provides a unified API, uses Promise’s chained calls to keep the whole request process organized, and enhances some additional functionality.

The Axios library is a nice third library, worth reading the source code for. You’ll learn a lot, too. Thank you so much for sticking with us.

Not only are there several interesting topics to study, such as:

  1. How does Axios set the request timeout
  2. How does Axios implement the proxy