Skip to content

深入promise

面试官:你如何理解 promise,能手写一个么? 答:。。。。

要了解 promise 的细节需要看 promise A+ 规范。链接名称 英文太多了,不看了直接上手写源码

原生 Promise 的使用

javascript
let p = new Promise((resolve, reject) => {
  resolve('resolve');
  reject('reject');
});

// then 是有2个参数的。 但平时我们一般只用1个 这里需要注意下
p.then(
  (res) => {
    console.log(res);
  },
  (err) => {
    console.log(err);
  }
);

p.catch((err) => {
  console.log(err);
});

开始尝试手写 Promise

1.搭建代码框架

第一步创建搭建代码框架

  • 这里需要在构造函数传入一个执行器 ,并立即执行
  • then 方法需要传入 2 个参数,用于执行返回结果 onFulfilled, onRejected
  • 创建 resolve, reject 方法用于触发状态扭转
js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  // 状态
  status = PENDING;
  // 返回的值
  value = null;
  // 失败后返回的值
  reason = null;
  constructor(executor) {
    // 执行器  new 的时候立即执行
    executor(this.resolve, this.reject);
  }

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
    }
  };
  reject = (err) => {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = err;
    }
  };

  then = (onFulfilled, onRejected) => {
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    } else if (this.status === REJECTED) {
      onRejected(this.reason);
    }
  };
}

module.exports = MyPromise;

测试一下

js
const MyPromise = require('./promise1');

// 测试1
let p = new MyPromise((resolve, reject) => {
  resolve('do resolve');
  reject('do reject');
});

p.then(
  (res) => {
    console.log(res);
  },
  (err) => {
    console.log(err);
  },
);

// 结果 do resolve

2.加入异步

第一步处理同步任务没问题但是处理异步任务就 gg

javascript
// 测试异步
let p = new MyPromise1((resolve, reject) => {
  setTimeout(() => {
    resolve('do resolve');
  }, 10);
});

p.then(
  (res) => {
    console.log(res);
  },
  (err) => {
    console.log(err);
  }
);
//  结果 : 空
  • 因为状态扭转在异步中执行的,then 无法探查到, 我们加入一个回调队列,用于将回调方法存起来, 等状态扭转后再执行
js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  // 状态
  status = PENDING;
  // 返回的值
  value = null;
  // 失败后返回的值
  reason = null;
  // 缓存成功回调
  onFulfilledCallback = [];
  // 缓存失败回调
  onRejectedCallback = [];

  constructor(executor) {
    // 执行器  new 的时候立即执行
    executor(this.resolve, this.reject);
  }

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
      // TODO:当状态改变后,执行回调队列的方法
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(this.value);
      }
    }
  };
  reject = (err) => {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = err;
      // TODO:当状态改变后,执行回调队列的方法
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(this.reason);
      }
    }
  };

  then = (onFulfilled, onRejected) => {
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    } else if (this.status === REJECTED) {
      onRejected(this.reason);
    } else if (this.status === PENDING) {
      // TODO: 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来 等到执行成功失败函数的时候再传递
      this.onFulfilledCallback.push(onFulfilled);
      this.onRejectedCallback.push(onRejected);
    }
    // 加入异步
  };
}

module.exports = MyPromise;

测试一下

js
// 主线程代码立即执行,setTimeout 是异步代码,then 会马上执行,这个时候判断
// Promise 状态,状态是 Pending,然而之前并没有判断等待这个状态

const MyPromise2 = require('./promise2');

let p = new MyPromise2((resolve, reject) => {
  setTimeout(() => {
    resolve('do resolve');
  }, 10);
});

p.then(
  (res) => {
    console.log(res);
  },
  (err) => {
    console.log(err);
  },
);

// 结果 do resolve

// 测试多次调用
const promise = new MyPromise2((resolve, reject) => {
  setTimeout(() => {
    resolve('success');
  }, 2000);
});

promise.then((value) => {
  console.log(1);
  console.log('resolve', value);
});

promise.then((value) => {
  console.log(2);
  console.log('resolve', value);
});

promise.then((value) => {
  console.log(3);
  console.log('resolve', value);
});

// 结果
// 1
// resolve success
// 2
// resolve success
// 3
// resolve success

3.实现链式调用

  • then 需要支持链式调用, 那么他就需要返回一个 新的 promise 对象, 而不能是 this
    • 因为如果返回了 this 那么两个 promise 的状态相同, 内部状态是不能改变的,
    • 而且每次使用 promise 的参数值是取决于上一次 then 的返回值,所以不能使用 this
  • 新返回的 promise 由于需要在 init 之后使用 , 否则语法报错 , 所以需要加入一个微任务 queueMicrotask(()=>{ //处理新创建的 promise })
  • 由于 then 的 return 需要作为下一次 then 的参数, 所以需要进行判断
    • 如果 return 的是一个新的 promise 那么就需要判断他的状态
    • 如果是普通 value 那么直接 resolve 掉
  • 如果 then 返回的是自己则报错
js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  // 状态
  status = PENDING;
  // 返回的值
  value = null;
  // 失败后返回的值
  reason = null;
  // 缓存成功回调
  onFulfilledCallback = [];
  // 缓存失败回调
  onRejectedCallback = [];

  constructor(executor) {
    // 执行器  new 的时候立即执行
    executor(this.resolve, this.reject);
  }

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(this.value);
      }
    }
  };
  reject = (err) => {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = err;
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(this.reason);
      }
    }
  };

  then = (onFulfilled, onRejected) => {
    // TODO: 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
    const promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // TODO:创建微任务 等待 promise 初始化完成
        // 否则语法报错 不能在 init 之前使用promise
        queueMicrotask(() => {
          // 链式调用这里的 res 就可能是:一个新的 promise 对象 || 普通返回值
          const res = onFulfilled(this.value);
          // TODO:对返回值进行处理
          resolvePromise(promise, res, resolve, reject);
        });
      } else if (this.status === REJECTED) {
        // TODO:创建微任务 等待 promise 初始化完成
        // 否则语法报错 不能在 init 之前使用promise
        queueMicrotask(() => {
          // 链式调用这里的 res 就可能是:一个新的 promise 对象 || 普通返回值
          const res = onRejected(this.value);
          // TODO:对返回值进行处理
          resolvePromise(promise, res, resolve, reject);
        });
        onRejected(this.reason);
      } else if (this.status === PENDING) {
        this.onFulfilledCallback.push(onFulfilled);
        this.onRejectedCallback.push(onRejected);
      }
    });

    return promise;
  };
}

function resolvePromise(self, x, resolve, reject) {
  // ADD:
  if (x === self) {
    return reject(
      new TypeError('MyPromise Chaining cycle detected for promise #<Promise>'),
    );
  }

  // 判断x是不是 MyPromise 实例对象
  if (x instanceof MyPromise) {
    // 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
    // x.then(value => resolve(value), reason => reject(reason))
    // 简化之后
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

module.exports = MyPromise;

测试一下

js
const MyPromise = require('./promise3');

// 实现 链式调用
/**
 * then 方法要链式调用那么就需要返回一个 Promise 对象
 * then 方法里面 return 一个返回值作为下一个 then 方法的参数,
 * 如果是 return 一个 Promise 对象,那么就需要判断它的状态
 */

const promise1 = new MyPromise((resolve, reject) => {
  // 目前这里只处理同步的问题
  resolve('success');
});

promise1
  .then((value) => {
    console.log('resolve', value);
    return new MyPromise((resolve) => {
      resolve('do inner promise ');
    });
  })
  .then((value) => {
    console.log('resolve', value);
  });

// 结果
// resolve success
// resolve do inner promise

//TODO: 如果 then 返回自己的 promise 的话 就会发生循环调用 ,
// 使用原生 promise 会报 TypeError: Chaining cycle detected for promise #<Promise>
const promise2 = new MyPromise((resolve, reject) => {
  resolve(100);
});
const p1 = promise2.then(
  (value) => {
    console.log(value);
    return p1;
  },
  (err) => {
    // 这里不会执行
    console.log(1);
    console.log(err);
  },
);

p1.then(
  (res) => {},
  (err) => {
    // 这里会执行
    console.log(err);
  },
);
//  结果: TypeError: MyPromise Chaining cycle detected for promise #<Promise>

4.实现捕获异常,并变更状态

js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  // 状态
  status = PENDING;
  // 返回的值
  value = null;
  // 失败后返回的值
  reason = null;
  // 缓存成功回调
  onFulfilledCallback = [];
  // 缓存失败回调
  onRejectedCallback = [];

  constructor(executor) {
    // TODO:捕获异常
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(this.value);
      }
    }
  };
  reject = (err) => {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = err;
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(this.reason);
      }
    }
  };

  then = (onFulfilled, onRejected) => {
    // 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
    const promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        queueMicrotask(() => {
          // TODO:捕获异常
          try {
            const res = onFulfilled(this.value);
            resolvePromise(promise, res, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.status === REJECTED) {
        // TODO:捕获异常
        queueMicrotask(() => {
          try {
            const res = onRejected(this.reason);
            resolvePromise(promise, res, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.status === PENDING) {
        this.onFulfilledCallback.push(onFulfilled);
        this.onRejectedCallback.push(onRejected);
      }
    });

    return promise;
  };
}

function resolvePromise(self, x, resolve, reject) {
  if (x === self) {
    return reject(
      new TypeError('MyPromise Chaining cycle detected for promise #<Promise>'),
    );
  }

  // 判断x是不是 MyPromise 实例对象
  if (x instanceof MyPromise) {
    // 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
    // x.then(value => resolve(value), reason => reject(reason))
    // 简化之后
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

module.exports = MyPromise;

测试一下

js
const MyPromise = require('./promise4');

// TODO: 捕获执行器中的代码,如果执行器中有代码错误,那么 Promise 的状态要变为失败
const promise = new MyPromise((resolve, reject) => {
  // resolve(100);
  throw new Error('执行错误');
});
const p1 = promise.then(
  (value) => {
    console.log(value);
    return p1;
  },
  (err) => {
    console.log(1);
    console.log(err);
  },
);

const promise2 = new MyPromise((resolve, reject) => {
  resolve(100);
  // throw new Error('执行错误');
});
promise2
  .then(
    (value) => {
      console.log(0);
      throw new Error('then 执行错误');
    },
    (err) => {
      console.log(1);
      console.log(err);
    },
  )
  .then(
    (res) => {
      console.log(2);
    },
    (err) => {
      console.log(3);
      console.log(err);
    },
  );

5.then 为可选参数 修改 pending 状态代码 增加 resolve/reject 静态方法

  • 静态的 resolve/reject 需要返回 promise
js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// TODO:
// 增加异步状态下的链式调用
// 增加回调函数执行结果的判断
// 增加识别 Promise 是否返回自己
// 增加错误捕获

class MyPromise {
  // 状态
  status = PENDING;
  // 返回的值
  value = null;
  // 失败后返回的值
  reason = null;
  // 缓存成功回调
  onFulfilledCallback = [];
  // 缓存失败回调
  onRejectedCallback = [];

  // TODO: 增加静态方法
  static resolve(param) {
    if (param instanceof MyPromise) {
      return param;
    }

    return new MyPromise((resolve) => {
      resolve(param);
    });
  }

  // TODO: 增加静态方法
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }

  constructor(executor) {
    // 执行器  new 的时候立即执行
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(this.value);
      }
    }
  };
  reject = (err) => {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = err;
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(this.reason);
      }
    }
  };
  then = (onFulfilled, onRejected) => {
    /**
     * 上面我们处理 then 方法的时候都是默认传入 onFulfilled、onRejected 两个回调函数,
     * 但是实际上原生 Promise 是可以选择参数的单传或者不传,都不会影响执行。
     */
    onFulfilled =
      typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === 'function' ? onRejected : (value) => value;

    // 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
    const promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 如果是链式调用这里的 res 就是一个新的 promise 对象

        // const res = onFulfilled(this.value);
        // ADD:创建微任务 等待 promise 初始化完成
        // 否则语法报错 不能在 init 之前使用promise

        // resolvePromise(promise, res, resolve, reject);
        queueMicrotask(() => {
          // ADD:
          try {
            const res = onFulfilled(this.value);
            resolvePromise(promise, res, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.status === REJECTED) {
        queueMicrotask(() => {
          try {
            const res = onRejected(this.reason);
            resolvePromise(promise, res, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.status === PENDING) {
        this.onFulfilledCallback.push(() => {
          queueMicrotask(() => {
            try {
              const res = onFulfilled(this.value);
              resolvePromise(promise, res, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
        this.onRejectedCallback.push(() => {
          queueMicrotask(() => {
            try {
              const res = onRejected(this.reason);
              resolvePromise(promise, res, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
      }
    });

    return promise;
  };
}

function resolvePromise(self, x, resolve, reject) {
  if (x === self) {
    return reject(
      new TypeError('MyPromise Chaining cycle detected for promise #<Promise>'),
    );
  }

  // 判断x是不是 MyPromise 实例对象
  if (x instanceof MyPromise) {
    // 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
    // x.then(value => resolve(value), reason => reject(reason))
    // 简化之后
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

module.exports = MyPromise;

测试一下

js
const MyPromise = require('./promise5');

// TODO: 捕获执行器中的代码,如果执行器中有代码错误,那么 Promise 的状态要变为失败
const promise = new MyPromise((resolve, reject) => {
  // resolve(100);
  throw new Error('执行错误');
});
const p1 = promise.then(
  (value) => {
    console.log(value);
    return p1;
  },
  (err) => {
    console.log(1);
    console.log(err);
  },
);

const promise1 = new MyPromise((resolve, reject) => {
  resolve(100);
  // throw new Error('执行错误');
});

promise1
  .then()
  .then()
  .then((value) => console.log(value));

const promise2 = new MyPromise((resolve, reject) => {
  reject('err');
});

promise2
  .then()
  .then()
  .then(
    (value) => console.log(value),
    (reason) => console.log(reason),
  );

// 实现 resolve 与 reject 的静态调用
MyPromise.resolve()
  .then(() => {
    console.log(0);
    return MyPromise.resolve(4);
  })
  .then((res) => {
    console.log(res);
  });