手写一个 Promise


手写一个 Promise 首先要了解以下几点

  • Promise 是一个类,在执行这个类的时候需要传递一个执行器,执行器会立即执行
  • Promise 中有三种状态分别为成功(fulfilled)失败(rejected)等待 (pending)。改变状态只能是 pending 变为 fulfilled 或 rejected,一旦状态确定就不可更改。
  • resolve和reject函数是用来改变状态的。这两个函数会分别接收一个参数
  • then方法内部做的事情就是判断状态,如果状态为成功,调用成功的回调函数,如果是失败则调用失败的回调函数。then方法是被定义在原型对象中的。
  • then 成功回调有一个参数,表示 成功之后的值,失败回调也有一个参数,表示 失败后的原因

参考以上定义,开始动手。


// 定义状态常量
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败

class MyPromise {
  //executor 就是执行器
  constructor(executor) {
    executor(this.resolve, this.reject);
  }

  status = PENDING; // 默认为等待
  value = undefined; // 成功之后的值
  reason = undefined; // 失败后的原因

  //这里要用箭头函数,避免this问题
  resolve = (value) => {
    //将状态变更为成功
    if (this.set_status(FULFILLED)) {
      // 保存成功值
      this.value = value;
    }
  };

  reject = (reason) => {
    //将状态变更为失败
    if (this.set_status(REJECTED)) {
      // 保存失败原因
      this.reason = reason;
    }
  };

  set_status(status) {
    //判断状态是否为等待 ,因为 Promise状态一旦状态确定就不可更改。
    if (this.status === PENDING) {
      this.status = status;
      return true;
    }
    return false;
  }

  then(success_callback, fail_callback) {
    // 判断状态
    if (this.status === FULFILLED) {
      success_callback(this.value); // 将成功值返回
    } else if (this.status === REJECTED) {
      fail_callback(this.reason); // 将失败原因返回
    }
  }
}

// 测试
const promise = new MyPromise((resolve, reject) => {
  resolve("成功");
  reject("失败");
});

promise.then(
  (v) => {
    console.log(v);
  },
  (err) => {
    console.log(err);
  }
);
// 打印 ‘成功’ ,因为状态不可再改变所以失败不会被打印。

以上只是一个基本的 Promise 的实现,但我们并没有考虑到异步情况,接下来我们解决一下异步的问题。

那么如何判断异步呢?首先在判断状态的时候如果为等待,我们就把两个回调函数保存起来,等resolve或者reject 被触发的时候在调用回调函数即可。

//注意:为了方便展示,此段代码仅保留了变更后的方法。
class MyPromise {
  ...
  success_callback = undefined; // 成功回调
  fail_callback = undefined; // 失败回调

  resolve = (value) => {
    if (this.set_status(FULFILLED)) {
      this.value = value;

      //判断成功回调是否存,如果存在就调用
      if (this.success_callback) {
        this.success_callback(value); // 传递成功值
      }
    }
  };

  reject = (reason) => {
    if (this.set_status(REJECTED)) {
      this.reason = reason;

      //判断成功回调是否存,如果存在就调用
      if (this.fail_callback) {
        this.fail_callback(reason); // 传递失败原因
      }
    }
  };

 ...

  then(success_callback, fail_callback) {

    if (this.status === FULFILLED) {
      success_callback(this.value); 
    } else if (this.status === REJECTED) {
      fail_callback(this.reason); 
    } else {
      // 代码到此说明状态未发生改变。
      // 所以我们暂时把两个回调存储起来
      this.success_callback = success_callback;
      this.fail_callback = fail_callback;
    }
  }
}

那么异步的的问题我们解决好了,但还是有一个问题。同一个promise对象的then方法是可以被多次调用的,当then方法被多次调用之后,每一个then方法中传递的回调函数都是要被执行的。那我们在处理下这个问题。

 // 解决这个问题也很简单,只需要把存储的回调函数变成数组形式存储即可
 success_callback = []; // 成功回调
 fail_callback = []; // 失败回调

 resolve = (value) => {
    if (this.set_status(FULFILLED)) {
      this.value = value;

      while (this.success_callback.length) {
        this.success_callback.shift()(value); // 传递成功值
      }
    }
  };

  reject = (reason) => {
    if (this.set_status(REJECTED)) {
      this.reason = reason;

      while (this.fail_callback.length) {
        this.fail_callback.shift()(value); // 传递失败原因
      }
    }
  };

  then(success_callback, fail_callback) {
    if (this.status === FULFILLED) {
      success_callback(this.value); 
    } else if (this.status === REJECTED) {
      fail_callback(this.reason);
    } else {
      // push 到数组里
      this.success_callback.push(success_callback);
      this.fail_callback.push(fail_callback);
    }
  }

promise 的then方法是可以链式调用的,后面then方法拿到的值实际上是上一个then方法的返回值。

// 列如
promise.then((v) => {
    console.log(v);
   return 100;
},(err) => {
    console.log(err);
}).then(v => {
    console.log(v);
})

这个如何实现呢?我们要分成两个步骤来做,第一个要实现then的链式调用,第二就是如何把上一个then方法的返回值传递给后面的then。首相要清楚一点then是promise里的方法,如果我们想实现then方法的链式调用,那么每一个then方法它应该都返回一个promise对象。

  then(success_callback, fail_callback) {
    // 直接返回一个 新的 MyPromise 对象
    return new MyPromise(() => {
      if (this.status === FULFILLED) {
        success_callback(this.value); 
      } else if (this.status === REJECTED) {
        fail_callback(this.reason); 
      } else {
        this.success_callback.push(success_callback);
        this.fail_callback.push(fail_callback);
      }
    });
  }

接下来在处理上一个then 返回回来的值,我们用一个单独的方法来处理

then(success_callback, fail_callback) {
    const promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        const v = success_callback(this.value);
        this.resolve_promise(v, resolve, reject);
      } else if (this.status === REJECTED) {
        fail_callback(this.reason);
      } else {
        this.success_callback.push(success_callback);
        this.fail_callback.push(fail_callback);
      }
    });

    return promise;
  }

  resolve_promise(v, resolve, reject) {
    // 判断 v 是否是 MyPromise 对象,如果是 还要判断这个 MyPromise 的状态
    if (v instanceof MyPromise) {
      v.then(resolve, reject); // 如果是 MyPromise 它是什么状态 相应的我们就传递什么状态.
    } else {
      resolve(v);
    }
  }

then方法的链式调用其实还有一个判断需要处理一下的,就是返回的对象不能是当前的promise对象,不然就会触发循环调用。当然这个也非常好处理。

//我们只需要判断 success_callback 返回的值是否全等于 new 出的 promise 即可
resolve_promise(promise, v, resolve, reject) {
    if (promise === v) {
      return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    }
   ...
  }
}

到现在,我们还没有进行错误处理的,为了程序的健壮性,我们还是要去捕获错误并处理错误的。

//在执行器进行trycatch。如果出现错误调用reject并传递错误即可
constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
}
//then方法里同样也需要在成功回调函数里捕获错误
then(success_callback, fail_callback) {
    const promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 为了能在这里获取到 promise,讲一下代码变成异步
        setTimeout(() => {
          try {
            const v = success_callback(this.value); // 将成功值返回
            this.resolve_promise(promise, v, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
     ...
}

以上我们仅仅只处理了then 同步情况下成功的相关问题,我们封装下把其他的一起处理了。

...
class MyPromise {
  //executor 就是执行器
  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }

  status = PENDING; // 默认为等待
  value = undefined; // 成功之后的值
  reason = undefined; // 失败后的原因
  success_callback = []; // 成功回调
  fail_callback = []; // 失败回调

  //这里要用箭头函数,避免this问题
  resolve = (value) => {
    //将状态变更为成功
    if (this.set_status(FULFILLED)) {
      // 保存成功值
      this.value = value;
      //判断成功回调是否存,如果存在就调用
      while (this.success_callback.length) {
        const fn = this.success_callback.shift();
        fn();
      }
    }
  };

  reject = (reason) => {
    //将状态变更为失败
    if (this.set_status(REJECTED)) {
      // 保存失败原因
      this.reason = reason;
      //判断成功回调是否存,如果存在就调用
      while (this.fail_callback.length) {
        const fn = this.fail_callback.shift();
        fn();
      }
    }
  };

  set_status(status) {
    //判断状态是否为等待 ,因为 Promise状态一旦状态确定就不可更改。
    if (this.status === PENDING) {
      this.status = status;
      return true;
    }
    return false;
  }

  then(success_callback, fail_callback) {
   success_callback = success_callback ? success_callback : (value) => value;
    fail_callback = fail_callback ? fail_callback : (reason) => reason;
    const promise = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        // 为了能在这里获取到 promise,讲一下代码变成异步
        setTimeout(() => {
          this.resolve_promise(
            promise,
            success_callback,
            this.value,
            resolve,
            reject
          );
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          this.resolve_promise(
            promise,
            fail_callback,
            this.reason,
            resolve,
            reject
          );
        }, 0);
      } else {
        // 代码到此说明状态未发生改变。
        // 所以我们暂时把两个回调存储起来
        this.success_callback.push(() => {
          setTimeout(() => {
            this.resolve_promise(
              promise,
              success_callback,
              this.value,
              resolve,
              reject
            );
          }, 0);
        });
        this.fail_callback.push(() => {
          setTimeout(() => {
            this.resolve_promise(
              promise,
              fail_callback,
              this.reason,
              resolve,
              reject
            );
          }, 0);
        });
      }
    });

    return promise;
  }

  resolve_promise(promise, callback, value, resolve, reject) {
    try {
      const v = callback(value);
      if (promise === v) {
        return reject(
          new TypeError("Chaining cycle detected for promise #<Promise>")
        );
      }
      // 判断 v 是否是 MyPromise 对象,如果是 还要判断这个 MyPromise 的状态
      if (v instanceof MyPromise) {
        v.then(resolve, reject); // 如果是 MyPromise 它是什么状态 相应的我们就传递什么状态.
      } else {
        resolve(v);
      }
    } catch (error) {
      reject(error);
    }
  }
}

然后再把一些Promise其他方法补充一下。

// all 是通过 Promise点出来的所以 是个静态方法
  static all(array) {
    const result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      // all 的核心就是等待所有Promise都 resolve 的时候在调用自己的resolve
      // 所以我们定义一个添加所有结果的函数。每次添加完毕的时候判断是否已经全部添加完成。然后就可以调用resolve
      function add_data(key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          reject(result);
        }
      }

      for (let i = 0; i < array.length; i++) {
        const current = array[i];
        if (current instanceof MyPromise) {
          // MyPromise 类型
          current.then(
            (value) => add_data(i, value),
            (resolve) => reject(resolve)
          );
        } else {
          // 普通值
          add_data(i, current);
        }
      }
    });
  }
// resolve方法就相对简单一些
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    } else {
      return new MyPromise((resolve) => resolve(value));
    }
  }
  finally(callback) {
    // 得到当前 状态,以及不管什么状态调用回调函数
    return this.then(
      (value) => {
        return MyPromise.resolve(callback()).then(() => value); // 将结果向下传递
      },
      (reason) => {
        return MyPromise.resolve(callback()).then(() => {
          throw reason;
        }); // 将错误原因向下传递
      }
    );
  }
  catch(fail_callback) {
    return this.then(undefined, fail_callback);
  }

最后是 最终代码

// 定义状态常量
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败

class MyPromise {
  //executor 就是执行器
  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }

  status = PENDING; // 默认为等待
  value = undefined; // 成功之后的值
  reason = undefined; // 失败后的原因
  success_callback = []; // 成功回调
  fail_callback = []; // 失败回调

  //这里要用箭头函数,避免this问题
  resolve = (value) => {
    //将状态变更为成功
    if (this.set_status(FULFILLED)) {
      // 保存成功值
      this.value = value;
      //判断成功回调是否存,如果存在就调用
      while (this.success_callback.length) {
        const fn = this.success_callback.shift();
        fn();
      }
    }
  };

  reject = (reason) => {
    //将状态变更为失败
    if (this.set_status(REJECTED)) {
      // 保存失败原因
      this.reason = reason;
      //判断成功回调是否存,如果存在就调用
      while (this.fail_callback.length) {
        const fn = this.fail_callback.shift();
        fn();
      }
    }
  };

  set_status(status) {
    //判断状态是否为等待 ,因为 Promise状态一旦状态确定就不可更改。
    if (this.status === PENDING) {
      this.status = status;
      return true;
    }
    return false;
  }

  then(success_callback, fail_callback) {
    // 判断回调是否存在,不存在把值向下传递
    success_callback = success_callback ? success_callback : (value) => value;
    fail_callback = fail_callback ? fail_callback : (reason) => reason;
    const promise = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        // 为了能在这里获取到 promise,讲一下代码变成异步
        setTimeout(() => {
          this.resolve_promise(
            promise,
            success_callback,
            this.value,
            resolve,
            reject
          );
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          this.resolve_promise(
            promise,
            fail_callback,
            this.reason,
            resolve,
            reject
          );
        }, 0);
      } else {
        // 代码到此说明状态未发生改变。
        // 所以我们暂时把两个回调存储起来
        this.success_callback.push(() => {
          setTimeout(() => {
            this.resolve_promise(
              promise,
              success_callback,
              this.value,
              resolve,
              reject
            );
          }, 0);
        });
        this.fail_callback.push(() => {
          setTimeout(() => {
            this.resolve_promise(
              promise,
              fail_callback,
              this.reason,
              resolve,
              reject
            );
          }, 0);
        });
      }
    });

    return promise;
  }

  finally(callback) {
    // 得到当前 状态,以及不管什么状态调用回调函数
    return this.then(
      (value) => {
        return MyPromise.resolve(callback()).then(() => value); // 将结果向下传递
      },
      (reason) => {
        return MyPromise.resolve(callback()).then(() => {
          throw reason;
        }); // 将错误原因向下传递
      }
    );
  }

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

  resolve_promise(promise, callback, value, resolve, reject) {
    try {
      const v = callback(value);
      if (promise === v) {
        return reject(
          new TypeError("Chaining cycle detected for promise #<Promise>")
        );
      }
      // 判断 v 是否是 MyPromise 对象,如果是 还要判断这个 MyPromise 的状态
      if (v instanceof MyPromise) {
        v.then(resolve, reject); // 如果是 MyPromise 它是什么状态 相应的我们就传递什么状态.
      } else {
        resolve(v);
      }
    } catch (error) {
      reject(error);
    }
  }
  // all 是通过 Promise点出来的所以 是个静态方法
  static all(array) {
    const result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      // all 的核心就是等待所有Promise都 resolve 的时候在调用自己的resolve
      // 所以我们定义一个添加所有结果的函数。每次添加完毕的时候判断是否已经全部添加完成。然后就可以调用resolve
      function add_data(key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          reject(result);
        }
      }

      for (let i = 0; i < array.length; i++) {
        const current = array[i];
        if (current instanceof MyPromise) {
          // MyPromise 类型
          current.then(
            (value) => add_data(i, value),
            (resolve) => reject(resolve)
          );
        } else {
          // 普通值
          add_data(i, current);
        }
      }
    });
  }

  // resolve方法就相对简单一些
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    } else {
      return new MyPromise((resolve) => resolve(value));
    }
  }
}
  • 分享:
评论
发表评论 说点什么