Cancel the request

1. Why cancel the request?

The response time of a request is uncertain. If the number of requests is too many, the request initiated earlier may be responded later. Then we need to design a mechanism to ensure that late requests can be cancelled on the client side.

2. Figure out how to cancel a request:

  • Make a request A1 with the ID that identifies the request
  • Request A2 is initiated (An indicates the NTH request for the same interface A). The original request A1 is cancelled during the request A2 and is identified ascanceled(network processEnd of work)
  • Axios marks A1 as a request exception, enters the response processing phase (the response gets an error object), and request A1 ends.

3. What does Axios actually do?

If A request A is sent more than once (A1,A2… A cancelToken canceled when A(n+1) initiates A request that no more canceled.

4. Implementation

Axios website use case:

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

Check out the axios source code

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

Canceltoken.source () is a factory function that returns an object with the token and cancel attributes constructed from cancelToken. Let’s see what the CancelToken constructor does:

// axios/lib/cancel/CancelToken.js
function CancelToken(executor) { / / parameters executor

  // The parameter type is judged to be a function
  if (typeofexecutor ! = ='function') {
    throw new TypeError('executor must be a function.');
  }
  
  var resolvePromise;
  // The instance mounts a promise that will be resolved after the variable resolvePromise executes
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });


  var token = this; // Instances are tokens
  // The actuator executes, passing the function cancel to the outside world
  executor(function cancel(message) {
    if (token.reason) {
      // If the Token has the Reason attribute attached, the request under the token has been cancelled
      return;
    }
    // The token mounts the Reason attribute
    token.reason = new Cancel(message);
    // The outside world can set this token's promise to Resolved by executing resolvePromise
    resolvePromise(token.reason);
  });
}
Copy the code

So what is the use of this token promise resolved?

// axios/lib/adapters/xhr.js
    if (config.cancelToken) {
      // If the request is made with a cancelToken, the promise resolve of the token will cancel the request
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if(! request) {return;
        }

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

Best practices

Design a Pendings module that automatically cancels requests on each interception

// pendings.js
import Axios from 'axios';

const pendings = {};

export default {
  /** * add request */
  addPending(config) { 
    const { method, url, params, data } = config;
    const id = [method, url, JSON.stringify(params), JSON.stringify(data)].join('&');
    const cancel = pendings[id];
    config.cancelToken = config.cancelToken || new Axios.CancelToken(function excutor(cancel) { 
      if(! pendings[id]) { pendings[id] = cancel } })return config;
  },

  /** * remove request */
  removePending(config) { 
    const { method, url, params, data } = config;
    const id = [method, url, JSON.stringify(params), JSON.stringify(data)].join('&');
    const cancel = pendings[id];
    if (cancel && typeof cancel === 'function') { cancel(); pendings.delete(identify); }},/** * Clear all pending requests */
  clearPending() { 
    Object.keys(pendings).forEach(c= >pending[c]()); }},Copy the code

Use the axios filter in your request module:

// request.js
axios.interceptors.request.use((config) = > {
    removingPendings(config);
    addPendings(config);
    // ...
})

axios.interceptors.response.use((config) = > {
    removingPendings(config);
    addPendings(config);
    // ...
})
Copy the code

6. Summary

CancelToken is, in essence, a promise whose state changes at the user’s discretion (by executing the cancel function). At that point, the promise’s then callback is executed to cancel the request.

Reference 7.

  • How does Axios cancel a request
  • Axios source code analysis — cancel the request