Axios document case

Take a look at the Axios documentation for an example github.com/axios/axios…

  1. throughCancelToken.sourceThe factory function is cancelled
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
  1. throughCancelTokenConstructor to cancel
const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelTokennew CancelToken(function executor(c{
    // An executor function receives a cancel function as a parametercancel = c; })});// cancel the request
cancel();


Copy the code
  1. throughAbortControllerInterrupt request, this isfetchAPI, this article is no longer detailed, specific use can refer toDeveloper.mozilla.org/zh-CN/docs/…
const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response{
   / /...
});
// cancel the request
controller.abort()


Copy the code

Source code analysis

First, you need to download the Axios source from GitHub. If you don’t want to download it, you can also go to github1s.com/axios/axios… Take a look.

Canceltoken.source factory function

The CancelToken factory function helps us to instantiate an instance of CancelToken from the hidden inside.

So let’s look at the implementation of the factory function.

/ / file path Axios/lib/cancel/CancelToken. Js

// ...

/** * Returns an object that contains a new `CancelToken` and a function that, when called, * cancels the `CancelToken`. */
CancelToken.source = function source({
  var cancel;
  var token = new CancelToken(function executor(c{
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

module.exports = CancelToken;


Copy the code

The canceltoken. source factory function helps us to instantiate an instance of CancelToken, and then returns the instance we need to use (token) and the function to cancel the request (cancel).

Let’s dig deeper into CancelToken to see why the request was interrupted after the cancel function was executed.

CancelToken class

/ / file path Axios/lib/cancel/CancelToken. Js

// ...

/**
 * A `CancelToken` is an object that can be used to request cancellation of an operation.
 *
 * @class
 * @param {Function} executor The executor function.
 */
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;

  // eslint-disable-next-line func-names
  this.promise.then(function(cancel{
    if(! token._listeners)return;

    var i;
    var l = token._listeners.length;

    for (i = 0; i < l; i++) {
      token._listeners[i](cancel);
    }
    token._listeners = null;
  });

  // eslint-disable-next-line func-names
  this.promise.then = function(onfulfilled// ...
  };

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

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

// ...


Copy the code

As you can see from the examples in the documentation, a cancelToken is passed in when the request is made, which is an instance of cancelToken.

The instantiation calls the executor function we passed in, passing us the cancel function externally.

In addition, there is a promise attribute on this instance. When we call cancel, the promise will change from a pending state to a pity. The promise.then is triggered and all token._listeners are executed.

Where do the token._listeners come from?

Again, the answer is in the current file

/ / file path Axios/lib/cancel/CancelToken. Js

// ...

/** * Subscribe to the cancel signal */

CancelToken.prototype.subscribe = function subscribe(listener{
  // Reason if the value is not undefined, the request is cancelled. You can call the listener directly
  if (this.reason) {
    listener(this.reason);
    return;
  }

  if (this._listeners) {
    this._listeners.push(listener);
  } else {
    this._listeners = [listener]; }};// ...


Copy the code

A subscribe method is added to the prototype object of CancelToken to subscribe to the event of the cancellation request. If the request has been cancelled, the Listener is called immediately; otherwise, the listener is saved in the _Listeners array.

The listeners saved in the _Listeners are called when we call cancel, that is, cancel the request (see above).

At this time, the interrupt request operation is not seen, the specific logic is in the listener, the reason for this is to decouple, improve code reuse.

There’s also an unsubscribe that doesn’t unfold anymore.

This is the typical subscription publishing model.

Cancel the request

The fastest way is to search the config. CancelToken. Subscribe, so you can quickly locate to the realization of a request.

Just search the lib folder. You can see two places. One is lib/adapters/http.js and the other is lib/adapters/xhr.js.

Because Axios is an HTTP client that supports Node.js and browsers. The pattern of the adapter is applied here to accommodate both platforms. This article deals with cancellation requests, so instead of delving into that part, let’s just look at one of them.

// Axios/lib/adapters/xhr.js

// ...
    if (config.cancelToken || config.signal) {
      // Handle cancellation
      // eslint-disable-next-line func-names
      onCanceled = function(cancel{
        if(! request) {return; } reject(! cancel || (cancel && cancel.type) ?new Cancel('canceled') : cancel);
        request.abort();
        request = null;
      };

      config.cancelToken && config.cancelToken.subscribe(onCanceled);
      if (config.signal) {
        config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled); }}// ...

Copy the code

Since 187 line here, we can see the config. CancelToken. Subscribe (onCanceled) went to cancelToken register an interrupt request callback. request.abort(); Here request is an instance of XMLHttpRequest.

There is also a function called done that cancels onCanceled after a request succeeds or fails.

At this point the entire logic for canceling the request runs out. I simply draw a picture (draw a few hours 😭), I hope it will be convenient for everyone to understand.

Combined with Vue, the implementation leaves the page to cancel outstanding requests

The idea is to use a single object to manage all instances of CancelToken. The newly created cancelToken is saved to the object before the request is made, and the cancelToken instance is cleared when the request is complete (whether it succeeds or fails).

This, combined with vue-Router’s route guard, makes it possible to cancel all outstanding requests when leaving the page.

Some global interfaces require special handling, such as requests for user information, and these global interfaces can no longer interrupt requests when they leave the page.

I won’t show you the code here. I wrote a demo, have the need of small partners can check.

Github.com/AD-feiben/d…

Finally, learning source code is about learning good design in source code, and we need to think about how to apply that design to our projects, which is the most important point.

Hope the content of the article can provide you with a trace of help, if wrong, also hope to correct.