如何实现Promise,then,catch,finally,resolve,reject,all,allSettled,race,any?

112 阅读5分钟

本文将从Promise使用方法,实现原理出发,来实现手写MyPromise。

Promise的三个状态

  • 创建时是Pending
  • resolve进入调用栈执行后是Fullfilled
  • reject进入调用栈执行后是Rejected

1. 定义三种状态

const STATE = {
  PENDING: "pending",
  FULLFILLED: "fullfilled",
  REJECTED: "rejected",
};

如何创建MyPromise对象?

const p = new MyPromise(cb);

为了创建MyPromise对象,我们需要定义MyPromise类

2. 定义MyPromise类

class MyPromise {
  #state = STATE.PENDING;

  constructor(cb) {
    try {
      cb();
    } catch (err) {}
  }
}


需要向MyPromise对象内传入回调函数

const p = new MyPromise((resolve, reject) => {
  resolve("hi");
});

3. 处理创建Promise传入的回调函数

class MyPromise {
  #state = STATE.PENDING;
  #value;

  constructor(cb) {
    try {
      cb(this.#onResolve, this.#onReject);
    } catch (err) {
      this.#onReject();
    }
  }

  #onResolve = (value) => {
    this.#state = STATE.FULLFILLED;
    this.#value = value;
  };

  #onReject = (value) => {
    this.#state = STATE.REJECTED;
    this.#value = value;
  };
}

由于回调函数只能resolve一次或者reject一次,所以需要添加一些处理逻辑

#onResolve = (value) => {
    if (this.#state !== STATE.PENDING) return;
    this.#state = STATE.FULLFILLED;
    this.#value = value;
};

#onReject = (value) => {
    if (this.#state !== STATE.PENDING) return;
    this.#state = STATE.REJECTED;
    this.#value = value;
};

由于Promise的resolve无论如何都是异步执行的,也就是说需要等待同步代码执行完毕后才会执行resolve,所以我们需要把执行resolve的功能放入微任务队列。

  #onResolve = (value) => {
    queueMicrotask(() => {
      if (this.#state !== STATE.PENDING) return;
      this.#state = STATE.FULLFILLED;
      this.#value = value;
    });
  };

  #onReject = (value) => {
    queueMicrotask(() => {
      if (this.#state !== STATE.PENDING) return;
      this.#state = STATE.REJECTED;
      this.#value = value;
    });
  };

使用then来消耗MyPromise

p.then((value) => {
  console.log(value);
});

根据MDN的定义,then可传入两个callback,一个处理resolve,另一个处理reject

4. 实现then功能

  then(thenCb, catchCb) {
    if (thenCb != null) thenCb(this.#value);
    if (catchCb != null) catchCb(this.#value);
  }

但是这种方式有一个致命缺陷,还记得上文提到的resolve是异步吗,promise对象会在所有同步代码执行完毕后才会resolve,也就是说,才会把value赋值给this.#value。
又因为then这时是同步的,会先于resolve执行,所以这时执行thenCb(this.#value)传入的参数是undefined

所以我们需要想办法等待resolve执行完毕后再执行thenCb

解决方法:使用Array存入thenCb和catchCb

  #thenCallbacks = [];
  #catchCallbacks = [];

  #runCallbacks = () => {
    if (this.#state === STATE.FULLFILLED) {
      this.#thenCallbacks.forEach((thenCb) => {
        thenCb(this.#value);
      });
    }

    if (this.#state === STATE.REJECTED) {
      this.#catchCallbacks.forEach((catchCb) => {
        catchCb(this.#value);
      });
    }
  };

  then(thenCb, catchCb) {
    if (thenCb != null) {
      this.#thenCallbacks.push(thenCb);
    }
    if (catchCb != null) {
      this.#catchCallbacks.push(catchCb);
    }
    
    this.#runCallbacks(); //有时候resolve执行后又执行then,就不能等待resolve了
  }

调用栈中的同步代码执行完毕后,resolve将thenCallbacks中的thenCb全部抽出来执行

  #onResolve = (value) => {
    queueMicrotask(() => {
      if (this.#state !== STATE.PENDING) return;
      this.#state = STATE.FULLFILLED;
      this.#value = value;

      this.#runCallbacks();
    });
  };

  #onReject = (value) => {
    queueMicrotask(() => {
      if (this.#state !== STATE.PENDING) return;
      this.#state = STATE.REJECTED;
      this.#value = value;

      this.#runCallbacks();
    });
  };

5、实现then链式调用

根据Promise定义,then需要返回一个新的Promise

   /**
   *
   * @param {Function} thenCb
   * @param {Function} catchCb
   * @returns {MyPromise}
   */
  then(thenCb, catchCb) {
    return new MyPromise((resolve, reject) => {
      this.#thenCallbacks.push((result) => {
        resolve(thenCb(result));
      });

      this.#catchCallbacks.push((reason) => {
        reject(catchCb(reason));
      });

      this.#runCallbacks();
    });
  }

这时候我们已经可以实现一个简单的链式调用.

const p = new MyPromise((resolve, reject) => {
  resolve("hi");
});
p.then((value) => {
  console.log(value); // hi
  return "ethan";
}).then((value) => {
  console.log(value); // ethan
});

以及reject捕获

const p = new MyPromise((resolve, reject) => {
  reject("error");
});
p.then(
  (value) => {
    console.log(value); 
    return "ethan";
  },
  (err) => {
    console.log(err);  // error
    return "new promise";
  }
).then((value) => {
  console.log(value); // new promise
});

6.实现catch

catch的实现非常简单

  /**
   *
   * @param {Function} catchCb
   * @returns {MyPromise}
   */
  catch(catchCb) {
    return this.then(undefined, catchCb);
  }

7. 实现finally

finally的定义是:无论resolve还是reject,都执行传入的callback,并且返回一个新的Promise,resolve的是传入finally的value

  /**
   * 无论resolve还是reject,都执行传入的callback,并将value传下去
   * @param {Function} cb 
   * @returns {MyPromise}
   */
  finally(cb) {
    return this.then(
      (result) => {
        cb();
        return result;
      },
      (reason) => {
        cb();
        return reason;
      }
    );
  }

8.实现catch的链式调用

目前catch有一个问题

const p = new MyPromise((resolve, reject) => {
  resolve("hi");
});

p.catch((reason) => {
  console.log(reason);
  return "ethan";
}).then((value) => {
  console.log(value);
});

此时的then接不住p的hi,所以我们需要在resolve的情况下忽略catch。
办法就是在then中添加逻辑,当thenCb为空时,就直接把then创建的MyPromise resolve掉,不需要调用catchCb了。

  then(thenCb, catchCb) {
    return new MyPromise((resolve, reject) => {
      const thenCallback = (result) => {
        //*****添加的代码
        if (thenCb == null) {
          resolve(result);
          return;
        }
        //****************
        try {
          resolve(thenCb(result));
        } catch (err) {
          reject(err);
        }
      };

      this.#thenCallbacks.push(thenCallback);

      const catchCallback = (reason) => {
        try {
          resolve(catchCb(reason));
        } catch (err) {
          reject(err);
        }
      };

      this.#catchCallbacks.push(catchCallback);

      this.#runCallbacks();
    });
  }

同理,当出现下述情况时,也需要在then中添加逻辑

const p = new MyPromise((resolve, reject) => {
  reject("hi");
});

p.then((reason) => {
  console.log(reason);
  return "ethan";
}).catch((value) => {
  console.log(value);
});

我们需要忽略掉then,将rejected的状态以及reason传下去

then(thenCb, catchCb) {
    return new MyPromise((resolve, reject) => {
      const thenCallback = (result) => {
        if (thenCb == null) {
          resolve(result);
          return;
        }

        try {
          resolve(thenCb(result));
        } catch (err) {
          reject(err);
        }
      };

      this.#thenCallbacks.push(thenCallback);

      const catchCallback = (reason) => {
        //*****添加的代码
        if (catchCb == null) {
          reject(reason);
          return;
        }
        //*****添加的代码

        try {
          resolve(catchCb(reason));
        } catch (err) {
          reject(err);
        }
      };

      this.#catchCallbacks.push(catchCallback);

      this.#runCallbacks();
    });
  }

9.处理then返回Promise的情况

当then返回Promise时,会出现错误

const p = new MyPromise((resolve, reject) => {
  resolve("hi");
});

p.then((value) => {
  return new MyPromise((resolve, reject) => {
    resolve("ethan");
  });
}).then((value) => {
  console.log(value); 
  //MyPromise {#state: 'fullfilled', #value: 'ethan', …}
});

此时我们需要先消耗then中产生的MyPromise,然后传递给下方

  /**
   *
   * @param {MyPromise OR Object} value
   */
  #onResolve = (value) => {
    queueMicrotask(() => {
      if (this.#state !== STATE.PENDING) return;

        //*****添加的代码
      if (value instanceof MyPromise) {
        value.then(this.#onResolve, this.#onReject);
        return;
      }
        //*****添加的代码
      this.#state = STATE.FULLFILLED;
      this.#value = value;

      this.#runCallbacks();
    });
  };

这时console.log就正常了

const p = new MyPromise((resolve, reject) => {
  resolve("hi");
});

p.then((value) => {
  return new MyPromise((resolve, reject) => {
    resolve("ethan");
  });
}).then((value) => {
  console.log(value); // ethan
});

10. 实现静态方法Promise.resolve(),Promise.reject()

  static resolve = (value) => {
    return new MyPromise((resolve, reject) => {
      resolve(value);
    });
  };

  static reject = (reason) => {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  };

测试

const p = MyPromise.resolve("hi");
p.then((value) => {
  console.log(value); // hi
});

11. 实现静态方法Promise.all()

Promse.all()的定义是等待所有resolve返回array,只要有一个reject就reject。

/**
   * 等待所有resolve返回array,只要有一个reject就reject
   * @param {Array} promises
   * @returns {MyPromise}
   */
  static all = (promises) => {
    const results = [];
    let successPromises = 0;

    return new MyPromise((resolve, reject) => {
      promises.forEach((promise) => {
        promise
          .then((value) => {
            successPromises++;
            results.push(value);
            if (successPromises === promises.length) {
              resolve(results);
            }
          })
          .catch((err) => {
            reject(err);
          });
      });
    });
  };

测试

const p1 = new MyPromise((resolve, reject) => {
  resolve("hi");
});

const p2 = new MyPromise((resolve, reject) => {
  resolve("ethan");
});

Promise.all([p1, p2]).then((results) => {
  console.log(results); //  ['hi', 'ethan']
});

const p3 = new MyPromise((resolve, reject) => {
  reject("error");
});

Promise.all([p1, p2, p3])
  .then((results) => {
    console.log(results);
  })
  .catch((err) => {
    console.log(err); // error
  });

12. 实现Promise.allSettled()

/**
   * 无论resolve还是reject,都放进结果集中,结果集中的是MyPromise,而不是value
   * @param {Array} promises
   * @returns MyPromise
   */
  static allSettled = (promises) => {
    const results = [];
    return new MyPromise((resolve) => {
      promises.forEach((promise) => {
        promise
          .then(() => {
            results.push(promise);
          })
          .catch(() => {
            results.push(promise);
          });
      });
      resolve(results);
    });
  };

测试

const p1 = new MyPromise((resolve, reject) => {
  resolve("hi");
});

const p2 = new MyPromise((resolve, reject) => {
  resolve("ethan");
});

const p3 = new MyPromise((resolve, reject) => {
  reject("error");
});
MyPromise.allSettled([p1, p2, p3])
  .then((results) => {
    console.log(results);
  })
  .catch((err) => {
    console.log(err);
  });

image.png


13. 实现静态方法Promise.race()

/**
   * 返回第一个resolve或者reject的Promise的value
   * @param {Array} promises
   * @returns {MyPromise}
   */
  static race = (promises) => {
    return new MyPromise((resolve, reject) => {
      promises.forEach((promise) => {
        promise
          .then((value) => resolve(value))
          .catch((reason) => reject(reason));
      });
    });
  };

测试

const p1 = new MyPromise((resolve, reject) => {
  resolve("hi");
});

const p2 = new MyPromise((resolve, reject) => {
  resolve("ethan");
});

const p3 = new MyPromise((resolve, reject) => {
  reject("error");
});
MyPromise.race([p1, p2, p3])
  .then((results) => {
    console.log(results); // hi
  })
  .catch((err) => {
    console.log(err);
  });

14. 实现静态方法Promise.any()

/**
   * 有一个resolve就返回resolve的value,否则返回error
   * @param {Array} promises
   * @returns {MyPromise}
   */
  static any = (promises) => {
    return new MyPromise((resolve, reject) => {
      const results = [];
      let rejectedPromises = 0;

      promises.forEach((promise) => {
        promise
          .then((value) => {
            resolve(value);
          })
          .catch((err) => {
            rejectedPromises++;
            results.push(err);
            if (rejectedPromises === promises.length) {
              reject("AggregateError: All promises were rejected");
            }
          });
      });
    });
  };

测试

const p1 = new MyPromise((resolve, reject) => {
  resolve("hi");
});

const p2 = new MyPromise((resolve, reject) => {
  resolve("ethan");
});

const p3 = new MyPromise((resolve, reject) => {
  reject("error");
});

MyPromise.any([p1, p2, p3])
  .then((results) => {
    console.log(results); // hi
  })
  .catch((err) => {
    console.log(err);
  });

MyPromise.any([p3, p3, p3])
  .then((results) => {
    console.log(results); // AggregateError: All promises were rejected
  })
  .catch((err) => {
    console.log(err);
  });