Skip to content

Promise(二):手动实现一个 Promise

上一篇文章我们认识了 Promise 的基本用法,现在来尝试手动来实现一下简单的 Promise 功能。

简易版 Promise

javascript
/*创建三个变量来表示状态*/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function myPromise(fn) {
  /* that 保存 this ,避免 this 指向问题*/
  const that = this;
  /*value 用来保存 resolve 或 reject 中传入的值*/
  that.value = null;
  /*默认状态*/
  that.status = PENDING;
  /*
   *用于保存 then 中的回调
   *当执行完 Promise 时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
   */
  that.fulfilledCallback = [];
  that.rejectedCallback = [];

  function resolve(value) {
    if (that.status === PENDING) {
      /*改变status状态*/
      that.status = FULFILLED;
      /*保存传入的value值*/
      that.value = value;
      /*执行回调方法*/
      that.fulfilledCallback.forEach((fun) => fun(that.value));
    }
  }

  function reject(value) {
    if (that.status === PENDING) {
      /*改变status状态*/
      that.status = REJECTED;
      /*保存传入的value值*/
      that.value = value;
      /*执行回调方法*/
      that.rejectedCallback.forEach((fun) => fun(that.value));
    }
  }

  /*执行回调*/
  try {
    fn(resolve, reject);
  } catch (e) {
    reject(e);
  }
}

/*
 *考虑到所有的实例都要用到then方法,then方法得放在promise的原型链上。
 *当状态是PENDING状态时,该做什么?不执行回调,那就将回调方法分别放入不同的栈内,等待调用。
 *当状态为FULFILLED或者REJECTED时,则执行响应的方法即可。
 */
myPromise.prototype.then = function (resolveFun, rejectFun) {
  let _this = this;
  if (_this.status === PENDING) {
    /*等待状态,添加回调函数到栈中*/
    _this.fulfilledCallback.push(() => {
      resolveFun(_this.value);
    });

    _this.rejectedCallback.push(() => {
      rejectFun(_this.value);
    });
  }

  if (_this.status === FULFILLED) {
    resolveFun(_this.value);
  }

  if (_this.status === REJECTED) {
    rejectFun(_this.value);
  }
};

let pro = new myPromise((resolve, reject) => {
  console.log("myPromise");
  resolve("data");
});

pro.then((res) => {
  console.log(res);
});

好了,一个简易的 Promise 就完成了,我们来看一下结果:

优化

resolve 和 reject 的改造

根据 promise+规范改造 promise,传给 then 的应该是个 promise。 resolve 和 reject 的改造:

  • 对于 resolve 函数来说,首先需要判断传入的值是否为 Promise 类型
  • 为了保证函数执行顺序,需要将两个函数体代码使用 setTimeout 包裹起来
javascript
function resolve(value) {
  /*判断value值是否为Promise类型*/
  if (value instanceof myPromise) {
    return value.then(resolve, reject);
  }

  /*为保证函数执行顺序,将函数体包裹在setTimeout中*/
  setTimeout(() => {
    if (that.status === PENDING) {
      /*改变status状态*/
      that.status = FULFILLED;
      /*保存传入的value值*/
      that.value = value;
      /*执行回调方法*/
      that.fulfilledCallback.forEach((fun) => fun(that.value));
    }
  }, 0);
}

function reject(value) {
  /*为保证函数执行顺序,将函数体包裹在setTimeout中*/
  setTimeout(() => {
    if (that.status === PENDING) {
      /*改变status状态*/
      that.status = REJECTED;
      /*保存传入的value值*/
      that.value = value;
      /*执行回调方法*/
      that.rejectedCallback.forEach((fun) => fun(that.value));
    }
  }, 0);
}

then 方法优化

javascript
myPromise.prototype.then = function (resolveFun, rejectFun) {
  let _this = this;
  /*新增一个变量 promise2,因为每个 then 函数都需要返回一个新的 Promise 对象,该变量用于保存新的返回对象*/
  let promise2 = null;
  resolveFun = typeof resolveFun === "function" ? resolveFun : (res) => res;
  rejectFun =
    typeof rejectFun === "function"
      ? rejectFun
      : (val) => {
          throw val;
        };
  if (_this.status === PENDING) {
    /*等待状态,添加回调函数到栈中*/
    return (promise2 = new myPromise((resole, reject) => {
      _this.fulfilledCallback.push(() => {
        try {
          let val = resolveFun(_this.value);
          resultPromise(promise2, val, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });

      _this.rejectedCallback.push(() => {
        try {
          let val = rejectFun(_this.value);
          resultPromise(promise2, val, resolve, reject);
        } catch (e) {
          reject(e);
        }

        rejectFun(_this.value);
      });
    }));
  }

  if (_this.status === FULFILLED) {
    return (promise2 = new myPromise((resole, reject) => {
      setTimeout(() => {
        try {
          let val = resolveFun(_this.value);
          resultPromise(promise2, val, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    }));
  }

  if (_this.status === REJECTED) {
    return (promise2 = new myPromise((resolve, reject) => {
      setTimeout(() => {
        try {
          let val = rejectFun(_this.value);
          resultPromise(promise2, val, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    }));
  }
};

如果 val 处于等待状态,Promise 需保持为等待状态直至 val 被执行或拒绝 如果 val 处于其他状态,则用相同的值处理 Promise

javascript
  resultPromise(promise, val, resolve, reject){
    /*为了防止回调地狱的情况,需要判断一下 promise2 与当前结果的关系
    * 如果相等,这说明出现回调地狱,就抛出一个错误
    */
    if(promise === val){
      return reject(new TypeError('循环引用') )
    }
    /*判断val类型*/
    if (val instanceof myPromise) {
      val.then(function (value) {
        resultPromise(promise, value, resolve, reject)
      }, reject)
    }
    /*创建一个变量 bool 用于判断是否已经调用过函数*/
    let bool = false;
    if(val !== null && (typeof val === 'object' || typeof val === 'function') ){
      try{
        /*因为每个 promsie 都会有一个 then 方法,用变量 then 来保存它;如果出现错误,直接执行 reject 方法*/
        let then = val.then;
        /*判断then方法的类型,如果是 function;就认为返回的是一个 promise,否则就是一个普通的值,直接调用 resolve 方法*/
        if( typeof then === 'function'){
          /*让then方法执行,判断执行的结果,如果成功就走 resolve ;失败就走 reject
          * 因为当前上下文的 this 指向的并不是 val,所以通过 call 方法改变this指向 val
          */
          then.call(val, (data)=>{
            if(bool) return;
            bool = true;
            /*通过递归解析,重复执行,直到返回的结果不是一个 promise 为止*/
            resultPromise(promise2, data, resolve, reject)
          },(e)=>{
            if(e) return;
            bool = true;
            reject(e)
          })
        }else {
          resolve(val)
        }
      } catch(e){
        if(bool) return;
        bool = true;
        reject(e)
      }
    }else {
      /*如果当前的值不是一个函数也不是一个对象,那说明是一个普通值,就直接进入下一次的成功方法*/
      resolve(val)
    }
  }

完整代码

以上就是修改的内容了,来将代码整理一下:

javascript
/*创建三个变量来表示状态*/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function myPromise(fn) {
  /* that 保存 this ,避免 this 指向问题*/
  const that = this;
  /*value 用来保存 resolve 或 reject 中传入的值*/
  that.value = null;
  /*默认状态*/
  that.status = PENDING;
  /*
   *用于保存 then 中的回调
   *当执行完 Promise 时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
   */
  that.fulfilledCallback = [];
  that.rejectedCallback = [];

  function resolve(value) {
    /*判断value值是否为Promise类型*/
    if (value instanceof myPromise) {
      return value.then(resolve, reject);
    }

    /*为保证函数执行顺序,将函数体包裹在setTimeout中*/
    setTimeout(() => {
      if (that.status === PENDING) {
        /*改变status状态*/
        that.status = FULFILLED;
        /*保存传入的value值*/
        that.value = value;
        /*执行回调方法*/
        that.fulfilledCallback.forEach((fun) => fun(that.value));
      }
    }, 0);
  }

  function reject(value) {
    /*为保证函数执行顺序,将函数体包裹在setTimeout中*/
    setTimeout(() => {
      if (that.status === PENDING) {
        /*改变status状态*/
        that.status = REJECTED;
        /*保存传入的value值*/
        that.value = value;
        /*执行回调方法*/
        that.rejectedCallback.forEach((fun) => fun(that.value));
      }
    }, 0);
  }

  /*执行回调*/
  try {
    fn(resolve, reject);
  } catch (e) {
    reject(e);
  }
}

myPromise.prototype.then = function (resolveFun, rejectFun) {
  let _this = this;
  /*新增一个变量 promise2,因为每个 then 函数都需要返回一个新的 Promise 对象,该变量用于保存新的返回对象*/
  let promise2 = null;
  resolveFun = typeof resolveFun === "function" ? resolveFun : (res) => res;
  rejectFun =
    typeof rejectFun === "function"
      ? rejectFun
      : (val) => {
          throw val;
        };
  if (_this.status === PENDING) {
    /*等待状态,添加回调函数到栈中*/
    return (promise2 = new myPromise((resole, reject) => {
      _this.fulfilledCallback.push(() => {
        try {
          let val = resolveFun(_this.value);
          resultPromise(promise2, val, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });

      _this.rejectedCallback.push(() => {
        try {
          let val = rejectFun(_this.value);
          resultPromise(promise2, val, resolve, reject);
        } catch (e) {
          reject(e);
        }

        rejectFun(_this.value);
      });
    }));
  }

  if (_this.status === FULFILLED) {
    return (promise2 = new myPromise((resole, reject) => {
      setTimeout(() => {
        try {
          let val = resolveFun(_this.value);
          resultPromise(promise2, val, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    }));
  }

  if (_this.status === REJECTED) {
    return (promise2 = new myPromise((resolve, reject) => {
      setTimeout(() => {
        try {
          let val = rejectFun(_this.value);
          resultPromise(promise2, val, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    }));
  }
};

function resultPromise(promise, val, resolve, reject) {
  /*为了防止回调地狱的情况,需要判断一下 promise2 与当前结果的关系
   * 如果相等,这说明出现回调地狱,就抛出一个错误
   */
  if (promise === val) {
    return reject(new TypeError("循环引用"));
  }
  /*判断val类型*/
  if (val instanceof myPromise) {
    val.then(function (value) {
      resultPromise(promise, value, resolve, reject);
    }, reject);
  }
  /*创建一个变量 bool 用于判断是否已经调用过函数*/
  let bool = false;
  if (val !== null && (typeof val === "object" || typeof val === "function")) {
    try {
      /*因为每个 promsie 都会有一个 then 方法,用变量 then 来保存它;如果出现错误,直接执行 reject 方法*/
      let then = val.then;
      /*判断then方法的类型,如果是 function;就认为返回的是一个 promise,否则就是一个普通的值,直接调用 resolve 方法*/
      if (typeof then === "function") {
        /*让then方法执行,判断执行的结果,如果成功就走 resolve ;失败就走 reject
         * 因为当前上下文的 this 指向的并不是 val,所以通过 call 方法改变this指向 val
         */
        then.call(
          val,
          (data) => {
            if (bool) return;
            bool = true;
            /*通过递归解析,重复执行,直到返回的结果不是一个 promise 为止*/
            resultPromise(promise2, data, resolve, reject);
          },
          (e) => {
            if (e) return;
            bool = true;
            reject(e);
          }
        );
      } else {
        resolve(val);
      }
    } catch (e) {
      if (bool) return;
      bool = true;
      reject(e);
    }
  } else {
    /*如果当前的值不是一个函数也不是一个对象,那说明是一个普通值,就直接进入下一次的成功方法*/
    resolve(val);
  }
}

执行结果

现在来使用一下,看下结果:

javascript
function p1() {
  let p1 = new myPromise((resolve, reject) => {
    resolve(111);
  });
  return p1;
}
function p2() {
  let p2 = new myPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(222);
      console.log("第二次");
    }, 2000);
  });
  return p2;
}

function p3() {
  let p3 = new myPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(333);
      console.log("第三次");
    }, 3000);
  });
  return p3;
}

p1()
  .then(
    (res) => {
      console.log(res);
      return p2();
    },
    (err) => {
      console.log("err", err);
    }
  )
  .then((res) => {
    console.log(res);
    return p3();
  })
  .then((res) => {
    console.log(res);
    return "直接返回数据";
  })
  .then((res) => {
    console.log(res);
  });

打印结果如下: