An overview of the

In front end development, we often encounter situations where we need to send asynchronous requests. The use of a functional, interface perfect HTTP request library, can greatly reduce our development costs, improve our development efficiency.

Axios is a very popular HTTP request library in recent years. Currently, it has more than 40K star on GitHub and has been recommended by many big names.

Today, we’ll take a look at how Axios is designed and what we can learn from it. As I write this, the version of Axios is 0.18.0. We take this version of the code as an example, to carry out specific source reading and analysis. All axios source files are currently in the lib folder, so the paths we refer to below refer to those in the lib folder.

The main contents of this paper are as follows:

  • How to use Axios
  • How the core modules of Axios are designed and implemented (request, interceptor, retraction)
  • What are the lessons of Axios’s design

How to use Axios

To understand the design of Axios, we first need to look at how Axios is used. Let’s introduce the FOLLOWING AXIOS API with a simple example.

Send the request

axios({
  method:'get'.url:'http://bit.ly/2mTM3nY'.responseType:'stream'
})
  .then(function(response) {
  response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))});Copy the code

This is an official API example. As you can see from the above code, the use of Axios is similar to jQuery’s Ajax in that it continues by returning a Promise (or a callback to success, but a Promise or await is recommended).

This code example is so simple that I won’t bore you with it, but let’s look at adding a filter function.

Add Interceptors

// Add a request interceptor. Note that there are two functions, one succeeds and one fails. The reason for this will be explained later
axios.interceptors.request.use(function (config) {
    // The request is processed before sending
    return config;
  }, function (error) {
    // Request error post-processing
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Process the response data
    return response;
  }, function (error) {
    // Post processing of the response error
    return Promise.reject(error);
  });
Copy the code

As we can see from the above example, before the request is sent, we can do data processing on the request’s Config parameter; After the request responds, we can also do specific operations on the returned data. At the same time, we can do specific error handling when the request fails and when the response fails.

Cancel the HTTP request

When performing search-related functions, we often need to send requests frequently to perform data queries. Typically, the next time a request is sent, we need to cancel the previous request. Therefore, cancellation request related functionality is also an advantage. The example code for axios to cancel a request is as follows:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // handle error}}); axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
Copy the code

As you can see from the above example, Axios is using a cancellation proposal based on a CancelToken. The proposal, detailed here, has since been withdrawn. The specific withdrawal implementation method will be explained in the source code analysis section.

How are the core modules of AXIOS designed and implemented

From the example above, I believe you have a general idea of how to use Axios. Next, we will analyze the design and implementation of AXIOS in terms of modules. Below are the axios related files that we will cover in this blog. If you are interested in reading the Clone code in conjunction with the blog, you can further understand the related modules.

HTTP request module

As a core module, the code associated with axios sending requests is located in the core/ dispatchrequst.js file. Due to the limited space, here I choose some key source code for a simple introduction:

module.exports = function dispatchRequest(config) {
    throwIfCancellationRequested(config);

    // Other source code

    // The default Adapter is a module that determines whether Node or XHR is used for sending requests
    var adapter = config.adapter || defaults.adapter; 

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

        // Other source code

        return response;
    }, function onAdapterRejection(reason) {
        if(! isCancel(reason)) { throwIfCancellationRequested(config);// Other source code

            return Promise.reject(reason);
        });
};
Copy the code

As we can see from the above code and example, the dispatchRequest method obtains the config.adapter to get the module that sent the request. We can also replace the native module by passing in a conforming Adapter function. But it’s also a loosely coupled extension point).

In the default.js file, we can see the adapter selection logic based on the properties and constructors that are specific to the current container.

function getDefaultAdapter() {
    var adapter;
    // Only Node.js has a class of variable type Process
    if (typeofprocess ! = ='undefined' && Object.prototype.toString.call(process) === '[object process]') {
        // Node.js request module
        adapter = require('./adapters/http');
    } else if (typeofXMLHttpRequest ! = ='undefined') {
        // The browser request module
        adapter = require('./adapters/xhr');
    }
    return adapter;
}
Copy the code

Axios XHR module is relatively simple, for the XMLHTTPRequest object encapsulation, we will not introduce more here, interested students can read the code in the Adapters /xhr.js file.

Interceptor module

With the HTTP request sending module implemented by dispatchRequest, let’s take a look at how Axios handles request and response interceptors. Let’s take a look at the request function, the unified entry point for requests in AXIos.

Axios.prototype.request = function request(config) {
    
    // Other code

    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

This function is the entry point for AXIos to send a request, and since the implementation of this function is quite long, I’ll briefly describe the design idea:

  1. Chain is an execution queue. The initial value of this queue is a Promise with a config parameter.
  2. In the chain execution queue, the initial send request function is inserteddispatchReqeustAnd the correspondingundefined. I need to add one laterundefinedThis is because in a Promise, you need a success and a fail callback function from the codepromise = promise.then(chain.shift(), chain.shift());You can see that. As a result,dispatchReqeustandundefinedWe can be a pair of functions.
  3. In the chain execution queue, the function that sends the requestdispatchReqeustIt’s in the middle. It’s preceded by a request interceptor that passesunshiftMethods Put in; It is followed by the response interceptor that passespushPut in. Notice that these functions are put in pairs, so two at a time.

From the request code above, we have a rough idea of how to use the interceptor. Next, let’s look at how to cancel an HTTP request.

Cancel request module

The cancellation request related module is in the Cancel/ folder. Let’s look at the relevant key code.

First, let’s look at the metadata Cancel class. It is a class that records the cancellation status.

    function Cancel(message) {
      this.message = message;
    }
 
    Cancel.prototype.toString = function toString() {
      return 'Cancel' + (this.message ? ':' + this.message : ' ');
    };
 
    Cancel.prototype.__CANCEL__ = true;
Copy the code

In the CancelToken class, it cancelates the HTTP request by passing a Promise.

function CancelToken(executor) {
    if (typeofexecutor ! = ='function') {
        throw new TypeError('executor must be a function.');
    }

    var resolvePromise;
    this.promise = new Promise(function promiseExecutor(resolve) {
        resolvePromise = resolve;
    });

    var token = this;
    executor(function cancel(message) {
        if (token.reason) {
            // Cancellation has already been requested
            return;
        }

        token.reason = new Cancel(message);
        resolvePromise(token.reason);
    });
}

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

In the adapter/xhr.js file, there is the corresponding code to cancel the request:

if (config.cancelToken) {
    // Wait for cancellation
    config.cancelToken.promise.then(function onCanceled(cancel) {
        if(! request) {return;
        }

        request.abort();
        reject(cancel);
        // Reset the request
        request = null;
    });
}
Copy the code

Combining the above example of canceling HTTP requests with this code, let’s briefly describe the implementation logic:

  1. In a request that might need to be canceled, we called the source method during initialization, which returned oneCancelTokenClass A and A function cancel.
  2. In instance A returned by the source method, A promise is initialized in the pending state. After we pass the entire instance A to Axios, the promise is used as A trigger to cancel the request.
  3. When the cancel method returned by the source method is called, the promise state in instance A changes from pending to fulfilled, immediately triggering the then callback, which triggers the cancellation logic of AXIosrequest.abort().

What are the lessons of Axios’s design

Send the processing logic of the request function

As mentioned in the previous section, Axios does not treat the dispatchRequest function that sends a request as a special function. Instead, it is treated as a non-discriminatory method and placed in the middle of the queue to ensure consistency in queue processing and improve code readability.

Processing logic of Adapter

In the adapter processing logic, axios does not treat HTTP and XHR modules (one for Node.js and the other for browser) as its own modules and directly consume the HTTP and XHR modules in the dispatchRequest. Instead, it is imported by default in the default.js file by configuration. This not only ensures the low coupling between the two modules, but also preserves the leeway for future users to customize the request sending module.

Cancel the processing logic of HTTP requests

In the logic to cancel the HTTP request, Axios cleverly uses a Promise as a trigger, passing the resolve function externally as an argument in the callback. This ensures that the internal logic is consistent, and that when a cancellation request is required, the sample data of the relevant class does not need to be changed directly, and the intrusion into other modules is avoided to the greatest extent.

conclusion

This paper introduces axiOS related usage, design ideas and implementation methods in detail. The above article provides an insight into Axios’ design thinking, as well as learning lessons about module encapsulation and interaction in Axios’ code.

Due to space, this article only breaks down and introduces the core modules of AXIOS. If you are interested in other code, you can go to GitHub to check it out.

If you have any questions or comments, please feel free to leave a comment.