浅谈promise的实现

113 阅读4分钟

为啥要写这篇文章?第一个是这个源码本身并不难,第二个是通过书写源码加深对promise的理解,最重要的一点是面试会遇到。

首先实现promise基本代码 Promise 作为构造函数 里面传递一个函数,传递的函数里面再传递两个函数,于是写出一下代码

    class MyPromise {
      constructor(excutor) {
        const resolve = (res) => {};
        const reject = (reason) => {};
        excutor(resolve, reject);
      }
    }

由于Promise内部维护了三个状态,分别为Pending,Resolve,Rejected。且每个promise的状态一定是由pending转化为resolve 或者rejected 状态,且不可逆,并且由pending转化后的状态都会有对应的数据,于是改造代码,增加状态

const MyPromise = (() => {
  //为了外部无法修改内部状态,这里使用IFFE 生成闭包,同时利用Sysbol 做私有属性
  //这里定义三个状态,使用变量替代硬编码
  const PENDING = "pending",
    SUCCESS = "success",
    Fail = "fail";
  //这里记录当前promise的状态
  const promiseStatu = Symbol("statu");
  //这里记录 成功的数据 或者 失败的理由
  const promiseValue = Symbol("promiseValue");

  return class {
    constructor(excutor) {
      this[promiseStatu] = PENDING;

      this[promiseValue] = undefined;

      const resolve = (data) => {
        this.changeStatu(SUCCESS, data);
      };

      const reject = (reason) => {
        this.changeStatu(Fail, reason);
      };

      excutor(resolve, reject);
    }

    changeStatu(statu, data) {
      //这里改变状态 只有状态是pending才改变
      if (this[promiseStatu] === PENDING) {
        this[promiseStatu] = statu;
        this[promiseValue] = data;
      }
    }
  };
})();

接下来我们实现then 方法。

const MyPromise = (() => {
  //为了外部无法修改内部状态,这里使用IFFE 生成闭包,同时利用Sysbol 做私有属性
  //这里定义三个状态,使用变量替代硬编码
  const PENDING = "pending",
    SUCCESS = "success",
    Fail = "fail";
  //这里记录当前promise的状态
  const promiseStatu = Symbol("statu");
  //这里记录 成功的数据 或者 失败的理由
  const promiseValue = Symbol("promiseValue");
  //这里保存 在then 函数里 res 和 rej 这两个函数
  const promiseResCallback = Symbol("promiseResCallback");
  const promiseRejCallback = Symbol("promiseRejCallback");

  return class {
    constructor(excutor) {
      this[promiseStatu] = PENDING;
      this[promiseValue] = undefined;
      this[promiseResCallback] = [];
      this[promiseRejCallback] = [];

      const resolve = (data) => {
        this.changeStatu(SUCCESS, data);
      };

      const reject = (reason) => {
        this.changeStatu(Fail, reason);
      };

      excutor(resolve, reject);
    }

    then(res, rej) {
      if (this[promiseStatu] === SUCCESS) {
        setTimeout(() => {
          res(this[promiseValue]);
        }, 0);
      } else if (this[promiseStatu] === Fail) {
        setTimeout(() => {
          rej(this[promiseValue]);
        }, 0);
      } else {
        //pending 状态,这里如果是pending,那么then 参数里面的两个函数应该被保存起来,
        //在之前的resolve,或者reject的函数 里面进行调用
        this[promiseResCallback].push(res);
        this[promiseRejCallback].push(rej);
      }
    }

    catch(rej) {
      if (this[promiseStatu] === Fail) {
        rej(this[promiseValue]);
      } else if (this[promiseStatu] === PENDING) {
        this[promiseRejCallback].push(rej);
      }
    }

    changeStatu(statu, data) {
      //这里改变状态 只有状态是pending才改变
      if (this[promiseStatu] === PENDING) {
        this[promiseStatu] = statu;
        this[promiseValue] = data;
        if (statu === SUCCESS) {
          this[promiseResCallback].forEach((element) => {
            setTimeout(() => {
              element(data);
            }, 0);
          });
        } else {
          this[promiseRejCallback].forEach((element) => {
            setTimeout(() => {
              element(data);
            }, 0);
          });
        }
      }
    }
  };
})();

then 方法的完善

  • 第一是then方法的需要返回一个promise 实现链式调用? 这样的话一定是要在Promise的then方法里面返回一个新的Promise
  • 第二是then方法如果实现then穿透? 可以对参数做默认处理,如果没有传递参数,可以默认一个参数,第一个 默认为 data=>data 第二个也可以做同样的默认, 不过在穿透时做一个值得比较,如果是undefined则无需穿透吗,否则穿透,于是最终生成代码
const MyPromise = (() => {
  //为了外部无法修改内部状态,这里使用IFFE 生成闭包,同时利用Sysbol 做私有属性
  //这里定义三个状态,使用变量替代硬编码
  const PENDING = "pending",
    SUCCESS = "success",
    Fail = "fail";
  //这里记录当前promise的状态
  const promiseStatu = Symbol("statu");
  //这里记录 成功的数据 或者 失败的理由
  const promiseValue = Symbol("promiseValue");
  //这里保存 在then 函数里 res 和 rej 这两个函数
  const promiseResCallback = Symbol("promiseResCallback");
  const promiseRejCallback = Symbol("promiseRejCallback");

  return class {
    constructor(excutor) {
      this[promiseStatu] = PENDING;
      this[promiseValue] = undefined;
      this[promiseResCallback] = [];
      this[promiseRejCallback] = [];

      const resolve = (data) => {
        this.changeStatu(SUCCESS, data);
      };

      const reject = (reason) => {
        this.changeStatu(Fail, reason);
      };

      excutor(resolve, reject);
    }
    // then 这里除了涉及发布订阅 还涉及then穿透以及链式调用
    then(res = (data) => data, rej = (reason) => reason) {
      return new MyPromise((resolve, reject) => {
        if (this[promiseStatu] === SUCCESS) {
          //setTimeout 模拟微队列
          setTimeout(() => {
            try {
              const result = res(this[promiseValue]);
              if (result instanceof MyPromise) {
                result.then(
                  (data) => {
                    resolve(data);
                  },
                  (reason) => {
                    reject(reason);
                  }
                );
              } else {
                resolve(result);
              }
            } catch (error) {
              reject(error);
            }
          }, 0);
        } else if (this[promiseStatu] === Fail) {
          setTimeout(() => {
            rej(this[promiseValue]);
          }, 0);
        } else {
          //pending 状态,这里如果是pending,那么then 参数里面的两个函数应该被保存起来,
          //在之前的resolve,或者reject的函数 里面进行调用
          this[promiseResCallback].push(() => {
            try {
              const result = res(this[promiseValue]);
              if (result instanceof MyPromise) {
                result.then(
                  (data) => {
                    resolve(data);
                  },
                  (reason) => {
                    reject(reason);
                  }
                );
              } else {
                resolve(result);
              }
            } catch (error) {
              reject(error);
            }
          }); //这里要知道res 执行的时机
          this[promiseRejCallback].push(() => {
            try {
              const result = rej(this[promiseValue]);
              if (result === undefined) {
                resolve();
              } else {
                reject(result);
              }
            } catch (error) {
              reject(error);
            }
          });
        }
      });
    }

    catch(rej) {
      if (this[promiseStatu] === Fail) {
        rej(this[promiseValue]);
      } else if (this[promiseStatu] === PENDING) {
        this[promiseRejCallback].push(rej);
      }
    }

    changeStatu(statu, data) {
      //这里改变状态 只有状态是pending才改变
      if (this[promiseStatu] === PENDING) {
        this[promiseStatu] = statu;
        this[promiseValue] = data;
        if (statu === SUCCESS) {
          this[promiseResCallback].forEach((element) => {
            setTimeout(() => {
              element(data);
            }, 0);
          });
        } else {
          this[promiseRejCallback].forEach((element) => {
            setTimeout(() => {
              element(data);
            }, 0);
          });
        }
      }
    }
  };
})();

最后就是一个 Promise.all 和 Promise.rice,大家不妨思考该如何实现。