preface

Freehand implementation of a Promise is front-end interview high-frequency topic, recently xiaobian read some blog articles, get to a simplest Promise code, only 20 lines of code to achieve the core asynchronous chain call function (not considering the failure callback), after a comb to understand the process recorded to share with you!

The implementation code

Here is the final code, you can take a look at it first, then slowly taste;

function Promise(fn) {//fn is a client-defined resolve, reject function passed in at instantiation
    this.successList = [];// Stores the success callback function successFn, which is pushed each time the THEN method executes
    const resolve = (value) = > {
        setTimeout(() = > {
            this.data = value;
            this.successList.forEach((successFn) = > successFn(value));
        });
    }
    fn(resolve);// We need to execute fn directly and pass it the required argument, which is the resolve function defined internally by promise
}

Promise.prototype.then = function (successFn) {
    // promise2
    return new Promise((resolve) = > {
        this.successList.push(() = > {
            const result = successFn(this.data);// Result corresponds to userPromise
            if (result instanceof Promise) {
                // Give userPromise a then method to perform the defined success callback
                result.then((res) = > {
                    resolve(res)
                });Result. then(resolve)
            } else {
                // If return is not a promise, some other value or no value,
                // Just pass this value to resolve, and successFn in promise2 is available for further processingresolve(result) } }); })};Copy the code

Analyze the process

Next, we begin to analyze step by step to understand and master the above source code. First, we start with the use of Promise, and the test cases are as follows:

The test case

Forget the chained call to THEN. Here is the simplest use case: Pass fn to Promise. When resolve is executed, the result is res in the argument passed to THEN.

const p = new Promise((resolve) = > {
    setTimeout(() = > {
        resolve(1);
    }, 500);
})

p.then((res) = > {
    console.log("Success", res);
})
Copy the code

So we started thinking, what would promises look like if we defined them ourselves?

  • 1. Is a constructor that takes and executes an externally defined function, called fn
  • 2. Provide a THEN method that takes a function, called successFn, and collects the successFn
  • 3. When fn is executed, resolve(1) needs to be provided
  • 4. When resolve(1) is executed, each successFn collected previously starts executing in sequence
  • 5. SuccessFn’s parameter is the success result value RES, which is 1 in this case, so the value 1 should be stored and then passed to successFn upon execution

Step 1: Basic implementation

Based on the above ideas, we implemented the following code:

function Promise(fn) {//fn is a client-defined resolve, reject function passed in at instantiation
    this.successList = [];// Stores the success callback function successFn, which is pushed each time the THEN method executes
    const resolve = (value) = > {
        this.data = value;
        this.successList.forEach((successFn) = > successFn(value));
    }
    fn(resolve);// We need to execute fn directly and pass it the required argument, which is the resolve function defined internally by promise
}

Promise.prototype.then = function (successFn) {
    this.successList.push(() = > {
        successFn(this.data);// this.data is "p.hen ((res) => {... }", we do not know the value of res until we execute resolve
    });
};
Copy the code

Step 2: Refine the code

Based on the above code, we continue to consider the following points:

  • 1. If a Promise is asynchronous, then the resolve implementation should also be asynchronous, using setTimeout.
  • 2. It’s time to think about itp.then(()=>{}).then(()=>{})This chain operation, thenThe then method must return a new PromiseIn order to continue calling its THEN method;
  • 3. To test the chain operation, add logic to then’s successFn function that returns a Promise.

So, here’s an example test:

// promise1
const p = new Promise((resolve) = > {
    setTimeout(() = > {
        resolve(1);
    }, 500);
})

p.then(function (res) {
    console.log(1 "succeed.", res);
    // userPromise
    return new Promise((resolve) = > {
        setTimeout(() = > {
            resolve(2);
        }, 500);
    });
}).then(function (res) {
    console.log("Successful 2", res);
})

// The desired result is
// "success 1 1"
// "Success 2 2"
Copy the code

Based on the first point above, you need to change the Promise constructor to the following code:

function Promise(fn) {//fn is a client-defined resolve, reject function passed in at instantiation
    this.successList = [];// Stores the success callback function successFn, which is pushed each time the THEN method executes
    const resolve = (value) = > {
        setTimeout(() = > {
            this.data = value;
            this.successList.forEach((successFn) = > successFn(value));
        });
    }
    fn(resolve);// We need to execute fn directly and pass it the required argument, which is the resolve function defined internally by promise
}
Copy the code

Then method implementation

Since the then method must return a promise to implement the chain operation, we can write it like this for now:

Promise.prototype.then = function (successFn) {
    // promise2
    return new Promise((resolve) = > {
    	console.log("push ----")
        this.successList.push(() = > {
            successFn(this.data); }); })};Copy the code

Next, to make it easier to explain, we name the promises in different locations. In the code, we call them promise1, promise2, and userPromise:

  • p=new Promise()=>promise1
  • Promise. Prototype. Then theReturn new Promise ()=>promise2
  • Test the samplep.then(function (res) {return new Promise(...) })In theReturn new Promise ()=>userPromise

While looking at the test examples, let’s comb through them:

  • 1. Perform the first stepp.then()Method to create and execute in promise2 at the same timethis.successList.push()The success callback function successFn is placed on promise1successListTo be executed, so the followingConsole. log(" success 1", res)Has not yet been implemented.
p.then(function (res) {
    console.log(1 "succeed.", res);
    // userPromise
    return new Promise((resolve) = > {
        setTimeout(() = > {
            resolve(2);
        }, 500);
    });
})
Copy the code
  • 2. The firstp.then()When it’s done, it’s returnedpromise2, you can proceed with the second onep.then(), so it is now atpromise2thesuccessListPut a callback with execution success;
  • 3. After 500ms,promise1Start to performresolve“, then take it outpromise1In thesuccessList, traversal executes the successful callback function in turn
  • 4. Perform the preceding stepssuccessFn(this.data)The following function is executed:
function (res) {
    console.log(1 "succeed.", res);
    // userPromise
    return new Promise((resolve) = > {
        setTimeout(() = > {
            resolve(2);
        }, 500);
    });
}
Copy the code

After printing “success 1 1”, create userPromise again, and execute the Fn function passed in userPromise, which looks like this:

(resolve) => {
    setTimeout(() = > {
        resolve(2);
    }, 500);
}
Copy the code

Resolve (2) wait 500ms to execute userPromise, and when successFn(this.data) completes, it creates and returns the userPromise.

But here’s the thing!

  • 5. UserPromise doesn’t have a then method to push a success callback into its successList, so resolve (2) has no success callback to execute!

  • 6. And, as shown in # 2 above, we give the successFn in the second P.chen (successFn) to promise2

So, here’s what to do!

  • 7. Give userPromise a then method to execute, make userPromise have a success callback, and notify promise2 to execute resolve () when userPromise succeeds.

  • 8. If userPromise resolves successfully, promise2 resolves as well, so that the successList placed on promise2 iterates over its function items.

Finally, after modification, the final version of the THEN method is as follows:

Promise.prototype.then = function (successFn) {
    // promise2
    return new Promise((resolve) = > {
        console.log("push ----")
        this.successList.push(() = > {
            const result = successFn(this.data);// Result corresponds to userPromise
            if (result instanceof Promise) {
                // Give userPromise a then method to perform the defined success callback
                result.then((res) = > {
                    resolve(res)
                });Result. then(resolve)
            } else {
                // If return is not a promise, some other value or no value,
                // Just pass this value to resolve, and successFn in promise2 is available for further processingresolve(result) } }); })};Copy the code

conclusion

Battle is a code that uses only the promise1 and userPromise externally and uses a promise2 internally to link the promise1 and userPromise together.

Shared the most simple Promise code ideas, I hope to help you! If there are omissions, please feel free to comment ~ 😊