You can also write your own Promise by hand.

Keywords: Front-end

I'm not going to write this article about the causes of Promise and the problems it solves. I just want to write an article about implementing my own Promise. If there is something wrong with the code and logic, please point it out. That's all. Let's get to the point.

Prerequisite: We need to know that Promise is based on Promises/A+ Canonical. Many of these variables and method names come from here. Let's start with the use of Promise and write a few test examples.

let promise = new Promise((resolve, reject) =>{
    // console.log("1");
    // resolve("Success");
    // reject("fail");
    // console.log("2");// First step


    // reject("fail");
    // resolve("Success");// The second step

    // setTimeout(() => {
    //     resolve("success");
    // }, 2000);

    throw new Error("Manual throwing error");// The fourth step
});
promise.then((value) => {
    console.log("then The first method is:"+value);
}, (err) => {
    console.log("then The second method is:"+err);
})
promise.then((value) => {
    console.log("then The first method is:"+value);
}, (err) => {
    console.log("then The second method is:"+err);
})
console.log("3");

First step output

  • 1
  • 2
  • 3
  • The first method: Success
  • The first method: Success

Second step output

  • 3
  • The second method: Failure
  • The second method: Failure

Step 3 Output

  • Three or two seconds later
  • The first method: success
  • The first method: success

Step 4 Output

  • 3
  • The second method: Error: manually throw an error
  • The second method: Error: manually throw an error

The final output of "success" indicates that the n is executed asynchronously

Based on the above examples, we can deduce the following points:

  • Promise is a constructor (using new)
  • Promise receives a parameter and the parameter is a function (we call it executor for convenience of description)
  • executor executes at new Promise
  • new Promise can support asynchronous behavior (step 3)
  • executor has two parameters (resolve,reject)
  • resolve and reject will not be passed in, indicating that they are provided by Promise content
  • Both resolve and reject are functions, and both receive a parameter. Look at the specification: the resolve receive parameter is called value, and the reject receive parameter is called reason.
  • There are then methods on each Promise instance
  • The then method is asynchronous
  • There are two parameters in the then method, onFulfilled and onRejected, which are successful callbacks and failed callbacks, respectively. Look here
  • In a Promise, resolve and reject will only execute one. Promise States is mentioned in the specification. You can see that
  • An instance of the same promise can call back all successful methods when it succeeds and all failed methods when it fails.
  • If errors are found, they will go into failure.

It looks a little messy with such a big stuff. We started writing our own Promise based on our conclusions. In the process of writing, the train of thought gradually became clear.

let myPromise = function (executor) {
    let self = this;//Cache this

    self.status = 'pending';// Changes in state management state can only be changed from pending to resolved or rejected. One thing cannot succeed and fail at the same time. So resolved and rejected cannot be transformed into each other.
    self.value = undefined;// Successful values are passed to resolve
    self.reason = undefined;//Failure causes passed to reject

    self.onResolvedCallbacks = [];// DepositthenSuccessful callback
    self.onRejectedCallbacks = []; // DepositthenA failed callback 
    // Let me show you that the third step is to use a timer. After executing the new Promise, it executesthenMethod, at this point will bethenThe method is cached and not executed: at this point the state is still pending. Wait until 2 seconds after the timer, and execute
    // When resolve|reject, the methods stored in the array are executed sequentially. Reference publish-subscribe mode

    function resolve(value) {
        // pending => resolved
        if (self.status === 'pending') {
            self.value = value;
            self.status = 'resolved';
            // Successful callbacks that execute caches in turn
            self.onResolvedCallbacks.forEach(fn => fn(self.value));
        }
    }

    function reject(reason) {
        // pending => rejected
        if (self.status === 'pending') {
            self.value = value;
            self.status = 'rejected';
            // Failed callbacks that execute caches in turn
            self.onRejectedCallbacks.forEach(fn => fn(self.reason));
        }
    }

    try {
        //executor executes when new Promise
        executor(resolve, reject);
    } catch (error) {
        reject(error);// When an exception occurs in executor, reject is executed directly
    }
}

// Each Promise instance hasthenMethod
Promise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;

    // Resolution was performed
    if (self.status === 'resolved') {
        // Perform a successful callback
        onFulfilled(self.value);
    }

    // reject was executed
    if (self.status === 'rejected') {
        // Callback for failed execution
        onRejected(self.reason);
    }

    // new Promise can support asynchronism as the default waiting state pending when neither resolve nor reject is executed
    if (self.status === 'pending') {
        // Cache successful callback
        self.onResolvedCallbacks.push(onFulfilled);
        // Callback for Cache Failure
        self.onRejectedCallbacks.push(onRejected);
    }
};

Note: This is the simplest version, because the power of Promise is the chain call. We are just embryonic, because of the time relationship. Let's get here first. Next time, we will implement a full version of Promises/A+based on this prototype.

For the first time, I hope you will support me.

Posted by JKinBlack on Tue, 24 Sep 2019 03:55:54 -0700