Handwritten Promise in plain language

A promise in normal use:

const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1");
  reject("reject1");
})

lcPromise.then(
  (res) => {
    
    
    console.log("res1: ", res);
  },
  (err) => {
    
    
    console.log("err1: ", err);
  }
);

lcPromise.then(
  (res) => {
    
    
    console.log("res2: ", res);
  },
  (err) => {
    
    
    console.log("err2: ", err);
  }
);

// 输出
// resolve1
// res1: resolve1
// res2: resolve1

basic structure

It can be seen from the above observation that:

  1. The Promsie constructor must accept a parameter function
  2. Parameter functions receive two functions as arguments (resolve, reject)
  3. promise has an instance method then
  4. The then method also receives two parameter functions (onFulfilled, onRejected)
  5. According to the Promise/A+ specification, promises have three states
  6. The specification stipulates that only in the pending state can resolve and reject be executed, and the corresponding state can be changed after execution
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;
    
    const resolve = value => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
      	this.status = PROMISE_STATUS_FULFILLED
      }
    }
    const reject = reason => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
        this.status = PROMISE_STATUS_REJECTED
      }
    }
    
    executed(resolve, reject)
  }
  
  then(onFulfilled, onRejected) {
    
    
    
  }
}

then method

We know that after executing resolve, the first parameter function in then will be executed, so the parameter function call in then must be in the function body of the resolve function, so that the resolve function can be executed, and then the parameter function in then will be executed accordingly.
And then can be called multiple times, and the multiple parameter functions received can be executed sequentially, so the parameter functions received by then must be collected .
And each parameter function of then can receive the actual parameter in resolve or reject.

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    // 收集 then 的参数
    this.onFulfilledFns = []
    this.onRejectedFns = []
    
    const resolve = value => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
      	this.status = PROMISE_STATUS_FULFILLED

        // 执行 then 中函数
        this.onFulfilledFns.forEach((fn) => fn(value));
      }
    }
    const reject = reason => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
        this.status = PROMISE_STATUS_REJECTED

        this.onRejectedFns.forEach((fn) => fn(reason));
      }
    }
    
    executed(resolve, reject)
  }
  
  then(onFulfilled, onRejected) {
    
    
    this.onFulfilledFns.push(onFulfilled)
    this.onRejectedFns.push(onRejected)
  }
}

When the above code is executed, you will find that when resolve is executed, the parameter function in then is not added to the onFulfilledFns array. Why?
Because new only executes the constructor method, before the then method is executed, the resolve method is executed, and naturally the parameter function of then cannot be collected.
So now the goal is to let the then method execute first, and then execute the call to the then parameter function in resolve.
You can use the event loop, resolve and then are synchronous tasks, add the part of resolve that calls the parameter function of then to the microtask queue, so that it can be called after the collection of then is executed .

const resolve = value => {
    
    
  if (this.status === PROMISE_STATUS_PENDING) {
    
    
    this.status = PROMISE_STATUS_FULFILLED
    // 加入微任务队列
    queueMicrotask(() => {
    
    
      // 执行 then 中函数
      this.onFulfilledFns.forEach((fn) => fn(value));
    })
      
  }
}
const reject = reason => {
    
    
  if (this.status === PROMISE_STATUS_PENDING) {
    
    
    this.status = PROMISE_STATUS_REJECTED
    queueMicrotask(() =>{
    
    
      this.onRejectedFns.forEach((fn) => fn(reason));
    })
  }
}

The call of the then parameter function in resolve is added to the micro queue, and the delay is executed after the synchronization task, which solves the problem of collection. Now what if the then function itself is in the macrotask? The microtask precedes the macrotask, so the parameters of the then method in the macrotask cannot be collected.

// 宏任务中调用 then,resolve 无法收集到 then 的参数函数
setTimeout(() => {
    
    
  lcPromise.then(
    (res) => {
    
    
      console.log("res3: ", res);
    },
    (err) => {
    
    
      console.log("err3: ", err);
    }
  );
})

Therefore, the then parameter function in the macro task cannot be executed in resolve . Because the resolve has been executed at this time, the then parameters after the resolve is executed must be executed in then itself.
After resolve is executed, the promise state becomes fulfilled, so the parameters collected after fulfilled must be executed by itself.
And the parameter function needs to get the actual parameter of resolve. Before the parameter function was executed in resolve, the actual parameter can be obtained. Now the parameter function is executed outside the resolve and cannot be obtained. Therefore, the actual parameter needs to be saved in the promise instance for sharing .

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    this.onFulfilledFns = []
    this.onRejectedFns = []

    this.value = undefined
    this.reason = undefined
    
    const resolve = value => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
      	this.status = PROMISE_STATUS_FULFILLED
        this.value = value
        queueMicrotask(() => {
    
    
          this.onFulfilledFns.forEach((fn) => fn(value));
        })
          
      }
    }
    const reject = reason => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
        this.status = PROMISE_STATUS_REJECTED
        this.reason = reason
        queueMicrotask(() =>{
    
    
          this.onRejectedFns.forEach((fn) => fn(reason));
        })
      }
    }
    
    executed(resolve, reject)
  }
  
  then(onFulfilled, onRejected) {
    
    
    // 1. 在 resolve 中执行
    this.onFulfilledFns.push(onFulfilled)
    this.onRejectedFns.push(onRejected)

    // 2. fulfilled 状态后收集的参数函数,在自身中执行
    if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
      onFulfilled(this.value)
    }
    if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
      onRejected(this.reason)
    }
  }
}

Now there is a problem, first look at the execution process of the test code:

const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1");
  reject("reject1");
})

lcPromise.then(
  (res) => {
    
    
    console.log("res1: ", res);
  },
  (err) => {
    
    
    console.log("err1: ", err);
  }
);

setTimeout(() => {
    
    
  lcPromise.then(
    (res) => {
    
    
      console.log("res3: ", res);
    },
    (err) => {
    
    
      console.log("err3: ", err);
    }
  );
})

// 结果:res1 执行了两遍
// res1:  resolve1 
// res1:  resolve1
// res3:  resolve1
  1. The new constructor, the constructor is executed, the parameter function executed starts to execute, and then resolve is executed.
  2. When executing resolve, change the state to fulfilled immediately after judging pending, and then add the rest to the microtask queue.
  3. Then execute reject, the pending judgment fails, jump out, the constructor synchronization task is executed, and the instance method then starts to be executed.
  4. The first then call starts to execute. In then, res1 and err1 are added to the collection array, and then the promise is judged as fulfilled, and then the parameter functions res1 and err1 are executed immediately. Herein lies the problem.

The first then is a synchronous task call. Its parameter function has been added to the array and waits for the microtask to execute in resolve, but now it is executed immediately in the synchronous task. It executes it twice. The problem is that the state of the promise becomes earlier, and the state of the promise cannot become fulfilled in the synchronization task, because then must be executed after resolve. The promise should change state in the microtask queue, so that the then judgment of the synchronous task is always pending and will not be executed immediately.

const resolve = value => {
    
    
  if (this.status === PROMISE_STATUS_PENDING) {
    
    
    this.value = value
    queueMicrotask(() => {
    
    
      // 保持同步任务中,promise 状态为 pending
      this.status = PROMISE_STATUS_FULFILLED
      this.onFulfilledFns.forEach((fn) => fn(value));
    })
      
  }
}
const reject = reason => {
    
    
  if (this.status === PROMISE_STATUS_PENDING) {
    
    
    this.reason = reason
    queueMicrotask(() =>{
    
    
      this.status = PROMISE_STATUS_REJECTED
      this.onRejectedFns.forEach((fn) => fn(reason));
    })
  }
}

// 结果:then 中成功和失败的参数函数都执行了
// res1:  resolve1
// err1:  reject1
// err3:  reject1

But there is another problem: resolve and reject are both synchronous tasks. If the state changes in them are in microtasks, they cannot lock each other. For example, execute resolve first, add part of the code to the microtask queue, jump out, and continue to execute reject. It is reasonable that reject should not execute valid code at this time, but the state of promise is still pending at this time, reject is not locked, and reject is executed normally. The code also joins the microtask queue. At that time, you will see that both the res parameter function and the err parameter function in then have been executed.
Therefore, we need to judge the status of the promise in the microtask queue to prevent each other from executing valid code in resolve and reject.

const resolve = value => {
    
    
  this.value = value
  queueMicrotask(() => {
    
    
    if (this.status !== PROMISE_STATUS_PENDING) return
    this.status = PROMISE_STATUS_FULFILLED
    this.onFulfilledFns.forEach((fn) => fn(value));
  })
}
const reject = reason => {
    
    
  this.reason = reason
  queueMicrotask(() =>{
    
    
    if (this.status !== PROMISE_STATUS_PENDING) return
    this.status = PROMISE_STATUS_REJECTED
    this.onRejectedFns.forEach((fn) => fn(reason));
  })
}

chain call

const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1");
  reject("reject1");
})
  .then(
    (res) => {
    
    
      console.log("res1: ", res);
    },
    (err) => {
    
    
      console.log("err1: ", err);
    }
  )
  .then(
    (res) => {
    
    
      console.log("res2: ", res);
    },
    (err) => {
    
    
      console.log("err2: ", err);
    }
  );

Why can then be chained?
Because then itself returns a promise. So we also return a promise in then.

then(onFulfilled, onRejected) {
    
    
  // 1. 在 resolve 中执行
  this.onFulfilledFns.push(onFulfilled);
  this.onRejectedFns.push(onRejected);

  // 2. fulfilled 状态后收集的参数函数,在自身中执行
  if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
    onFulfilled(this.value);
  }
  if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
    onRejected(this.reason);
  }

  return new LcPromise((resolve, reject) => {
    
    
    resolve("the second promise")
  })
}

Now then has returned a new promise that can be chained, but the actual parameter of resolve in the new promise should not be a fixed value. Normally, resolve in the new promise should receive the return value of the then onFulfilled parameter function in the first promise.
In order for resolve to get the return value of the previous then parameter function, you can add all the code in the previous then to the second promise , because the constructor is executed synchronously, so there is no difference from the outside.

  then(onFulfilled, onRejected) {
    
    
    return new LcPromise((resolve, reject) => {
    
    
      // 1. 在 resolve 中执行
      this.onFulfilledFns.push(onFulfilled);
      this.onRejectedFns.push(onRejected);
  
      // 2. fulfilled 状态后收集的参数函数,在自身中执行
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
        const value = onFulfilled(this.value);
        // resolve("the second promise")
        resolve(res)
  
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
        const reason = onRejected(this.reason);
        reject(reason)
      }
    })
  }

There is a serious problem with the above code. Under normal circumstances, the return value of the parameter function in the previous then, whether it is the return value of the parameter function onFulfilled or onRejected, they are passed into the onFulfilled of the next then as an actual parameter, that is, in the resolve in the next promise as an actual parameter. Instead of onFulfilled corresponding to resolve, onRejected corresponding to reject. Only the error thrown by the parameter function in the previous then will be passed to the reject of the next promise, which is the onRejected of the next then.
So in the onRejected of the above code, it should not be reject(reason), but resolve(reason). And both onFulfilled and onRejected must be wrapped by try...catch to pass the error information to onRejected of the next then.

then(onFulfilled, onRejected) {
    
    
    return new LcPromise((resolve, reject) => {
    
    
      // 1. 在 resolve 中执行
      this.onFulfilledFns.push(onFulfilled);
      this.onRejectedFns.push(onRejected);

      // 2. fulfilled 状态后收集的参数函数,在自身中执行
      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);
        }
      }
    });
  }

Now that the parameter functions called in then itself are handled, the second promise's resolve can easily get their return value, but the parameter functions that are added to the array and executed in the first promise's resolve What about the return values?
The first way is to save their return values ​​and share them, like this:

const resolve = (value) => {
    
    
  this.value = value
  queueMicrotask(() => {
    
    
    if (this.status !== PROMISE_STATUS_PENDING) return;
    this.status = PROMISE_STATUS_FULFILLED;
    this.firstPromiseValue = this.onFulfilledFns.forEach((fn) => fn(value));
  });

};
const reject = (reason) => {
    
    
  this.reason = reason;
  queueMicrotask(() => {
    
    
    if (this.status !== PROMISE_STATUS_PENDING) return;
    this.status = PROMISE_STATUS_REJECTED;
    this.firstPromiseReason = this.onRejectedFns.forEach((fn) => fn(reason));
  });
};

While it is possible, it can be cumbersome to manage variables. There is also a more ingenious way. Instead of directly adding the parameter function of then to the array, wrap the parameter function with a layer of functions and add it to the array, and then execute the parameter function in the function body of the wrapped function . This is equivalent to leaving the execution of the parameter function in then itself. What is added to the collection array is only a lead, and this lead is activated in resolve to start executing the parameter function.

// 1. 在 resolve 中执行
// this.onFulfilledFns.push(onFulfilled);
// this.onRejectedFns.push(onRejected);

this.onFulfilledFns.push(() => {
    
    
  try {
    
    
    const value = onFulfilled();
    resolve(value);
  } catch (error) {
    
    
    reject(error);
  }
});

this.onRejectedFns.push(() => {
    
    
  try {
    
    
    const reason = onRejected();
    resolve(reason);
  } catch (error) {
    
    
    reject(error);
  }
});

Basic implementation

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

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    // 收集 then 的参数
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    this.value = undefined;
    this.reason = undefined;

    const resolve = (value) => {
    
    
      this.value = value;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_FULFILLED;
        this.onFulfilledFns.forEach((fn) => fn(value));
      });
    };
    const reject = (reason) => {
    
    
      this.reason = reason;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_REJECTED;
        this.onRejectedFns.forEach((fn) =>
          fn(reason)
        );
      });
    };

    executed(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    
    
    return new LcPromise((resolve, reject) => {
    
    
      
      // 1. 在 resolve 中执行
      if (onFulfilled) this.onFulfilledFns.push(() => {
    
    
        try {
    
    
          const value = onFulfilled(this.value);
          resolve(value);
        } catch (error) {
    
    
          reject(error);
        }
      });

      if (onRejected) this.onRejectedFns.push(() => {
    
    
        try {
    
    
          const reason = onRejected(this.reason);
          resolve(reason);
        } catch (error) {
    
    
          reject(error);
        }
      });

      // 2. fulfilled 状态后收集的参数函数,在自身中执行
      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);
        }
      }
    });
  }
}

Test: onFulfilled of the second then

const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1")
  reject("reject1")
})
  .then(
    (res) => {
    
    
      console.log("res1: ", res);
      return "aaa";
    },
    (err) => {
    
    
      console.log("err1: ", err);
      return "bbb"
    }
  )
  .then(
    (res) => {
    
    
      console.log("res2: ", res);
    },
    (err) => {
    
    
      console.log("err2: ", err);
    }
  );

// 结果
res1:  resolve1
res2:  aaa

// 结果2(注释 resolve("resolve1"),走第一个 then 的 onRejected)
err1:  reject1
res2:  bbb

Test: onRejected of the second then

const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1");
  reject("reject1");
})
  .then(
    (res) => {
    
    
      console.log("res1: ", res);
      throw 'hhh'
      return "aaa";
    },
    (err) => {
    
    
      console.log("err1: ", err);
      throw "ikun"
      return "bbb";
    }
  )
  .then(
    (res) => {
    
    
      console.log("res2: ", res);
    },
    (err) => {
    
    
      console.log("err2: ", err);
    }
  );

// 结果
res1:  resolve1
err2:  hhh

// 结果2 (注释 resolve("resolve1"),走第一个 then 的 onRejected)
err1:  reject1
err2:  ikun

There are too many repeated codes above, you can extract and encapsulate them.

const execFunctionWithCatchError = (execFn, value, resolve, reject) => {
    
    
  try {
    
    
    const res = execFn(value)
    resolve(res)
  } catch (error) {
    
    
    reject(error)
  }
}
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

const execFunctionWithCatchError = (execFn, value, resolve, reject) => {
    
    
  try {
    
    
    const res = execFn(value);
    resolve(res);
  } catch (error) {
    
    
    reject(error);
  }
};

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    // 收集 then 的参数
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    this.value = undefined;
    this.reason = undefined;

    const resolve = (value) => {
    
    
      this.value = value;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_FULFILLED;
        this.onFulfilledFns.forEach((fn) => fn(this.value));
      });
    };
    const reject = (reason) => {
    
    
      this.reason = reason;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_REJECTED;
        this.onRejectedFns.forEach((fn) => fn(this.reason));
      });
    };

    executed(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    
    
    return new LcPromise((resolve, reject) => {
    
    
      // 1. 在 resolve 中执行
      if (onFulfilled) this.onFulfilledFns.push(() => {
    
    
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
      });

      if (onRejected) this.onRejectedFns.push(() => {
    
    
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
      });

      // 2. fulfilled 状态后收集的参数函数,在自身中执行
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
      }
    });
  }
}

catch method

The catch method is not in the specification, but ES6 is convenient for everyone to add by themselves.
Originally, the rejection of the promise is in the second parameter function of then, but now it is placed in the catch of then chain call. Then we can manually split then into two parts for execution.

catch(onRejected) {
    
    
  this.then(undefined, onRejected)
}
const lcPromise = new LcPromise((resolve, reject) => {
    
    
  reject("reject1");
})
  .then((res) => {
    
    
      console.log("res1: ", res);
    }, undefined)
  .catch((err) => {
    
    
    console.log("catch: ", err);
  });

But the above is actually problematic.
Originally, we want to call the onRejected callback function of the first promise, but because the second parameter in then is undefined, the collection array of the first promise is empty. . Now use catch to receive the onRejected callback.
But catch is called in a chain, and what actually calls catch is the new promise instance returned by the first then. That is, the onRejected actual parameter function received in the catch is added to the onRejectedFns array of the second promise .

Then how to execute the onRejected callback of the second promise through the rejection of the first promise?
The first way is to re-add this callback to the onRejectedFns array of the first promise. This works, but it is very troublesome.
The second method is not to let the first promise reject, because the second parameter of then is undefined, then do nothing, let the onRejected callback of then in the first promise and the onRejected callback of then in the second promise also It is the same as forming a chain call. It is equivalent to the onRejected in the first promise sending a signal, I was executed, and the real I in the second promise was executed quickly.
We already know that there is only one way to execute the onRejected of the second promise, that is, the first promise throws an exception.
Therefore, when the second parameter of then is undefined, we can add a function that throws an exception into the collection array and wait for execution, thus starting the real body execution in the second promise.

then(onFulfilled, onRejected) {
    
    
  // 当 onRejected 为 undefined 或其他空值时,就将它替换为一个抛出异常的函数
  onRejected = onRejected || ((err) => {
    
     throw err });
  return new LcPromise() {
    
    ...}
}

catch(onRejected) {
    
    
  this.then(undefined, onRejected)
}

finally method

Regardless of whether the promise succeeds or fails, the finally method must be executed. As can be seen from the above, what the promise actually executes is the then method. So the implementation of finally is nothing more than this:

finally(fns) {
    
    
  this.then(fns, fns)
}
const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1");
})
  .then((res) => {
    
    
    console.log("res1: ", res);
    return "aaa"
  })
  .catch((err) => {
    
    
    console.log("catch: ", err);
  })
  .finally(() => {
    
    
    console.log("finally");
  });

finally is also chained, so the catch should return the second promise.

catch(onRejected) {
    
    
  return this.then(undefined, onRejected);
}

finally will also encounter the same problem as catch: the call chain is broken. This time it is broken in this.(undefined, onRejected)the undefined of the catch.
Called by the above code, when the first promise resolves, onFulfilled in the first then is executed and has a return value. At this time, the onFulfilled execution in the second promise then will be started, that is, the execution in the catch this.(undefined, onRejected), but at this time onFulfilled is undefined, the call chain is broken, and the then execution of the third promise cannot be started, that is, finally The execution of this.then(fns, fns)finally cannot be executed.
Therefore, the onFulfilled parameter function in the **catch cannot be undefined, it needs to be an arbitrary function with a return value, which is used to start the subsequent onFulfilled execution**. The catch also executes then, so onFulfilled, which is the then method at the root, cannot be undefined, just change then.

then(onFulfilled, onRejected) {
    
    
  // 当 onRejected 为 undefined 或其他空值时,就将它替换为一个抛出异常的函数
  onRejected = onRejected || ((err) => {
    
     throw err });

  // 当 onFulfilled 为 undefined 或其他空值时,就将它替换为一个有返回值的函数
  onFulfilled = onFulfilled || ((value) => value);
  
  return new LcPromise() {
    
    ...}
}

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

fully realized

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

const execFunctionWithCatchError = (execFn, value, resolve, reject) => {
    
    
  try {
    
    
    const res = execFn(value);
    resolve(res);
  } catch (error) {
    
    
    reject(error);
  }
};

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    // 收集 then 的参数
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    this.value = undefined;
    this.reason = undefined;

    const resolve = (value) => {
    
    
      this.value = value;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_FULFILLED;
        this.onFulfilledFns.forEach((fn) => fn(this.value));
      });
    };
    const reject = (reason) => {
    
    
      this.reason = reason;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_REJECTED;
        this.onRejectedFns.forEach((fn) => fn(this.reason));
      });
    };

    executed(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    
    
    // 当 onRejected 为 undefined 或其他空值时,就将它替换为一个抛出异常的函数
    onRejected =
      onRejected ||
      ((err) => {
    
    
        throw err;
      });

    // 当 onFulfilled 为 undefined 或其他空值时,就将它替换为一个有返回值的函数
    onFulfilled = onFulfilled || ((value) => value);
    return new LcPromise((resolve, reject) => {
    
    
      // 1. 在 resolve 中执行
      if (onFulfilled)
        this.onFulfilledFns.push(() => {
    
    
          execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
        });

      if (onRejected)
        this.onRejectedFns.push(() => {
    
    
          execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
        });

      // 2. fulfilled 状态后收集的参数函数,在自身中执行
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
      }
    });
  }

  catch(onRejected) {
    
    
    return this.then(undefined, onRejected);
  }

  finally(fns) {
    
    
    this.then(fns, fns);
  }
}

Guess you like

Origin blog.csdn.net/qq_43220213/article/details/129761755
Recommended