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

In daily development, we often use Promise, but not many students are familiar with the principle of Promise, and they may be asked interview questions related to Promise in the interview. Today I’m going to take you with me to implement a simple version of Promise.

Realize the function

Before we can implement promises, we need to know what promises do, and here’s a quick overview

  • PromiseIs a class whose constructor takes a function and takes two arguments, respectivelyresolve,rejectMethod to change the currentPromiseThe state of the object.
  • PromiseThere are three statespending,fulfilled,rejected.
  • thenMethod for receivingPromiseThe success and failure case callback function returns a new onePromiseObject.
  • catchMethod for receivingPromiseThe callback function on failure, same as the second argument in then
  • finallyMethods in thePromiseExecutes the specified callback function at the end (regardless of state)fulfilledOr is itrejected)

Realize the Promise

Promise is an object that needs to be new, and we’ll create it using the class keyword.

Of course you can write function, but class is much clearer.

class MyPromise{}
Copy the code

A Promise needs to be passed a function when instantiated, received in the constructor, and executed.

class MyPromise {
  constructor(executor){ executor(); }}const p = new MyPromise(() = > {
  console.log("Function is executed.");
});

// The function is executed
Copy the code

Add resolve and Reason methods to the constructor for the two functions received on instantiation. And pass these two methods into the Executor method, which are used to change the state of the current Promise object.

class MyPromise {
  constructor(executor) {
    const resolve = (value) = > {
      console.log("resolve");
    };
    const reject = (reason) = > {
      console.log("reject"); }; executor(resolve, reject); }}const p = new MyPromise((resolve) = > {
  resolve(); 
});

// resolve
Copy the code

Define the values of the three states of the Promise object outside of the class. Initialize the current state of the Promise object as PROMISE_STATUS_PENDING in Construct

const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

// class MyPromise
constructor(executor) {
    this.status = PROMISE_STATUS_PENDING;
  }
Copy the code

The resolve and Reject methods are called externally to modify the current Promise state and store the value they receive in two variables.

// constructor function
this.value = undefined;
this.reason = undefined;
const resolve = (value) = > {
  if (this.status === PROMISE_STATUS_PENDING) {
    this.status = PROMISE_STATUS_FULFILLED;
    this.value = value; }};const reject = (reason) = > {
  if (this.status === PROMISE_STATUS_PENDING) {
    this.status = PROMISE_STATUS_REJECTED;
    this.reason = reason; }};Copy the code

Implement then methods

Create a new THEN method in the MyPromise class that accepts the onFulfilled and onRejected methods. In the constructor, create two arrays onimplemented FNS and onRejectedFns to store the callback function passed by then. (Promise instantiates objects that can be called multiple times by THEN, and two arrays should be created to hold the corresponding methods.)

// then
then(onFulfilled, onRejected) {
  onRejected =
    onRejected ||
    ((err) = > {
      throw err;
    });
  return new MyPromise((resolve, reject) = > {
    // If the state is already determined when then is called
    if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
      try {
        const value = onFulfilled(this.value);
        resolve(value);
      } catch(error) { reject(error); }}if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
      try {
        const reason = onRejected(this.reason);
        resolve(reason);
      } catch(error) { reject(error); }}if ((this.status = PROMISE_STATUS_PENDING)) {
      if (typeof onFulfilled === "function") {
        this.onFulfilledFns.push(() = > {
          try {
            const value = onFulfilled(this.value);
            resolve(value);
          } catch(error) { reject(error); }}); }if (typeof onRejected === "function") {
        this.onRejectedFns.push(() = > {
          try {
            const reason = onRejected(this.reason);
            resolve(reason);
          } catch(error) { reject(error); }}); }}}); }Copy the code

The constructor modifies the state of the current Promise object in the resolve, reject functions and executes the methods stored in the then methods.

const resolve = (value) = > {
  if (this.status === PROMISE_STATUS_PENDING) {
    queueMicrotask(() = > {
      if (this.status ! == PROMISE_STATUS_PENDING)return;
      this.status = PROMISE_STATUS_FULFILLED;
      this.value = value;
      this.onFulfilledFns.forEach((fn) = > fn(this.value)); }); }};const reject = (reason) = > {
  if (this.status === PROMISE_STATUS_PENDING) {
    queueMicrotask(() = > {
      if (this.status ! == PROMISE_STATUS_PENDING)return;
      this.status = PROMISE_STATUS_REJECTED;
      this.reason = reason;
      this.onRejectedFns.forEach((fn) = > fn(this.reason)); }); }};Copy the code
Test then method
const p = new MyPromise((resolve) = > {
  resolve(123); //
});

p.then((res) = > {
  console.log("res---", res);
  return "_island";
}).then((res) = > {
  console.log("res---", res);
});

// res: 123
// res--- _island
Copy the code

Implementing catch methods

Then (res=>res).catch(e=>e). Add a catch method to MyPromise that accepts a callback function. This callback is passed to the second argument in the THEN method.

catch(onRejected){
  return this.then(undefined,onRejected)
}
Copy the code

We also need to make a judgment call in the THEN method, which defaults to a handler when no second argument is passed.

then(onFulfilled, onRejected) {
  onRejected =onRejected || (err= >{ throw err })
  // ...
}
Copy the code
Test catch method
const p = new MyPromise((resolve,reject) = > {
  reject('Failed'); //
});

p.then((res) = > {
  console.log("res---", res);
  return "_island";
}).then(res= >{
  console.log('res---',res);
}).catch((err) = >{
  console.log('catch',err);
})

// Catch failed
Copy the code

Implementing the finally method

Finally is also simple to implement, executing the specified callback when the state of the Promise object changes. Add a finally method to MyPromise that accepts a callback function. This function is passed to the then method.

finally(onFinally) {
  this.then(
    () = > {
      onFinally();
    },
    () = >{ onFinally(); }); }Copy the code

It is worth noting that in the then method we also have to judge whether the ondepressing parameter has a value if it is not given a default value.

then(onFulfilled, onRejected) {
  onFulfilled = onFulfilled || (value= > value);
  // ...
}
Copy the code
Test finally method
p.then((res) = > {
  console.log("res---", res);
}).finally(() = > {
  console.log("finally");
});

// res--- result
// finally
Copy the code

conclusion

This simplified version of Promise is not a lot of code to implement, either, less than 100 lines. But it’s a little bit harder to understand. The then method is a complex way to implement this whole simplified version of a Promise, involving method judgments, and the order in which calls are made. It’s actually pretty easy to understand as long as you know the order of execution.