“This is the sixth day of my participation in the First Challenge 2022. For details: First Challenge 2022”

First published in my blog – Big Front-end Engineer growth path – [AXIos source] – cancel request cancel module read analysis

Cancelable promises Proposal package based on TC39, but this proposal has been cancelled by the sponsors themselves, it is said that because of the strong opposition within Google, IssueWhy was this proposal getting involved? Have a look.

First, environmental preparation

  • Axios version v0.24.0

  • The source code for Axios is available on the Github1s page

  • The debugging needs to be clone to the local PC

git clone https://github.com/axios/axios.git

cd axios

npm start

http://localhost:3000/
Copy the code

Second, function study

The Cancel module contains three files cancel, CancelToken, and isCancel

├ ─ the cancel// Cancel the request│ Cancel. Js │ CancelToken. Js │ isCancel. JsCopy the code

1. Cancel classes

A Cancel is an object that is thrown when an operation is canceled. A Cancel object is thrown when the operation is cancelled

"use strict";

/**
 * A `Cancel` is an object that is thrown when an operation is canceled.
 *
 * @class
 * @param {string=} message The message.
 */
function Cancel(message) {
    this.message = message;
}

Cancel.prototype.toString = function toString() {
    return "Cancel" + (this.message ? ":" + this.message : "");
};

Cancel.prototype.__CANCEL__ = true;

module.exports = Cancel;
Copy the code
  • It’s only 8 lines of code that implements oneCancel
  • Contains a prototype methodtoString, an instance attribute__CANCEL__
  • Instance attributes__CANCEL__Be set totrueIs the flag of the Cancel class

Tips: We know that a request can have three states: before, during, and at the end of a request, and the cancellation action can occur at any one of these stages. The cancel logic corresponding to different phases is different in AXIos, and the logical position of function execution is also different in this phase of the request. Therefore, when we initiate a cancel request, AXIos needs a mechanism to know where in the current phase of the network request to execute the corresponding cancel and return logic. The Cancel class is one such mechanism.

2. IsCancel function

"use strict";

module.exports = function isCancel(value) {
    return!!!!! (value && value.__CANCEL__); };Copy the code
  • Only 3 lines of code, passCancelInstance properties of__CANCEL__To judgevalueWhether it isCancelThe instance
  • Export aisCancelFunction, if the input parametervalnotCancelClasses will be returnedfalseOtherwise returntrue

3. CancelToken class

A CancelToken is an object that can be used to request cancellation of an operation

Refer to the article Lin Jingyi notepad mentioned this is a class that is make people confused about, just to see when I feel the same way, although after some analysis the author has analyzed in detail as possible (feel-good 🐶), but before watching or recommend not understand design patterns friend to learn about the publish/subscribe model, will help to understand

[2.1] Introduce the Cancel class

"use strict";

var Cancel = require("./Cancel");
Copy the code

[2.2] Internal function CancelToken

This. Promise. then

/**
 * 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) {
        var _resolve;
        // eslint-disable-next-line func-names
        var promise = new Promise(function (resolve) {
            token.subscribe(resolve);
            _resolve = resolve;
        }).then(onfulfilled);

        promise.cancel = function reject() {
            token.unsubscribe(_resolve);
        };

        return promise;
    };

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

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

Next, analyze it row by row

  • 1. Check whether the input parameter executor is of function type. If not, a TypeError will be raised

    • Why input parameterexecutorIt’s a function, right?
    • A:CancelTokenThrough the publish subscription model to achieve the transfer of cancellation information, subscribers want to subscribe to the eventcancel/cRegister with the dispatch centerCancelTokenWhen the adapter returns information, the publisher publishes it to the dispatch centerCancelToken, the dispatch center then uniformly schedules the processing code that the execution subscribers register with the dispatch centercancel. So that makes sense why this function is called, rightExecutor - actuators.
  • 2. Create a global variable resolvePromise

    • Why did you create this variable? Why create it globally?
    • A: To get itresolveFacilitates subsequent chain calls, and only global variablesThrough function scopegetresolve
  • Use new Promise() to create an instance of the promiseExecutor function. Executor is an asynchronous function that returns resolve and resolvePromise. You can use this.promise.then() to get the return value resolve or resolvePromise directly

    • Why createpromiseObjects?
    • Because the action of actually canceling the requestrequest.abort()In the adapterxhr.jsorhttp.jsIn, this is an asynchronous method, when the action occurs we can inresolveTo get the return value
  • 4. Create the global variable token to represent the current CancelToken instance

  • 5. The Executor executor executes the subscriber event cancel, whose message is passed in when the user calls it

  • 6. The subscriber event cancel is executed to determine whether the instance token has a reason attribute value. If it does, it returns the value, otherwise it uses new Cancel() to declare a Cancel class for Reason

    • Why usenew Cancel()Declare a cancel class toreasonThe assignment?
    • A: in order toreasonOne will be mounted on the prototype chain__CANCEL__Represents the cancel class and coordinates simultaneouslyresolvePromisePerform thethis.promiseInstance state changed tofulfilledThis means that the user actively cancels the information returned by the request and not because some other exception is returnedthis.promiseInstance state changed toreject)

CancelTokenThe constructor is a publish-subscribe function that is triggered outward by a publish-subscriberesolveOr throw an error (reject),PromiseThe chain structure was successfully obtainedresolveValue orcatchWhen an error is reached, the user’s input is returnedmessageOr stop continuing and perform an error callback.

[2.3] Add the prototype method throwIfRequested

/** * Throws a `Cancel` if cancellation has been requested. */
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
    if (this.reason) {
        throw this.reason; }};Copy the code
  • If a cancellation event has been triggered, an error message is thrown

【 2.4 】 the source

/** * 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,
    };
};
Copy the code
  • When initialized, the return value is an object containing the newCancelTokenobjecttokenAnd the functioncancel
  • The subscriber will subscribe to the event at execution timecRegister with the dispatch centerCancelTokenThe dispatch center is in the executorexecutorExecute subscriber events immediately in
  • cancelFunction byGlobal scopegetcThrough thereturnExpose the user to a portal to subscribe to events

[2.5] Added the prototype method subscribe

/** * Subscribe to the cancel signal */

CancelToken.prototype.subscribe = function subscribe(listener) {
    if (this.reason) {
        listener(this.reason);
        return;
    }

    if (this._listeners) {
        this._listeners.push(listener);
    } else {
        this._listeners = [listener]; }};Copy the code
  • Add a function that executes when subscribing to subscribe eventscancelIs executed to the current instance_listenersAppends the return value of the chained call to the

[2.6] Added the prototype method unsubscribe

/** * Unsubscribe from the cancel signal */

CancelToken.prototype.unsubscribe = function unsubscribe(listener) {
    if (!this._listeners) {
        return;
    }
    var index = this._listeners.indexOf(listener);
    if(index ! = = -1) {
        this._listeners.splice(index, 1); }};Copy the code
  • Add a function that executes at subscription time, subscription eventcancelIs executed to remove the current instance if the cancellation is repeated_listenersA chain call at the corresponding location on the property

Three, reference

1. Xianling Pavilion article detailed interpretation of Axios source code

2. Lin Jingyi article Lin Jingyi Notepad – Axios source analysis (two) : general tool method

3. Wakakawa’s article studied the overall architecture of AXIos source code to build his own request library

4. Jie Ling’s article reads axios source code in depth

5. Cape article design mode (3) : Observer mode and publish/subscribe mode difference