参考自:Promise 原理解析与实现(遵循Promise/A+规范)

setTimeout:

为了保证程序执行一致性的可测试性

  1. let cache;
  2. !function set() {
  3. // 这里可能是同步代码,也可能是异步代码
  4. cache = 100;
  5. setTimeout(() =>{
  6. cache = 100;
  7. })
  8. }();
  9. console.log(cache); // 如果是同步,这里就能得到 100,如果是异步,这里就是 undefined

所以添加一个 setTimeout 包裹,就都是异步代码,保证其一致性。

值的穿透例子:

  1. p.then()
  2. .then(function(value) {
  3. console.log(value);
  4. }, function(err) {
  5. console.log(err)
  6. })
  7. // 值仍然能够传递下去

循环引用例子:

  1. let p2 = p.then(function(value) {
  2. console.log(value);
  3. return p;
  4. }, function(err) {
  5. console.log(err)
  6. })

整体结构

  • Promise 是一个类
  • 需要传入一个回调函数
  • 实例中保存一个状态,初始就是 pending
  • 创建实例的时候,回调函数立即执行
  • 回调函数执行传入 res 和 rej 两个回调函数
  1. //=> 接受一个回调函数
  2. function Promise(fn) {
  3. let _this = this; // 为了确保函数中的 this 是实例,在函数中使用 _this
  4. this.status = 'pending';
  5. function resolve(value) {
  6. //...
  7. }
  8. function reject(err) {
  9. //...
  10. }
  11. //=> 创建实例时,立即执行 fn
  12. fn(resolve, reject);
  13. }

参数处理

由于 rej 和 res 两个函数接收的参数,需要在 then 中的回调函数中传入,所以需要在实例中保存起来。

  1. function Promise(fn) {
  2. //=> 传给成功和失败函数参数的初始值
  3. this.value = undefined;
  4. this.err = undefined;
  5. //=> 然后再 reject 或者 resolve 执行时,保存传入的参数
  6. function resolve(value) {
  7. //...
  8. _this.value = value;
  9. }
  10. function reject(err) {
  11. //...
  12. _this.err = err;
  13. }
  14. }

报错处理

如果 Promise 中的回调函数的代码执行过程中,出现报错,那么应该直接执行失败函数,并且把错误信息传入。

  1. function Promise(fn) {
  2. //...
  3. try {
  4. fn(resolve, reject);
  5. } catch(e) {
  6. reject(e);
  7. }
  8. }

状态处理

在执行了 rej 或者 res 函数后,需要把状态变更为 resolved 或者 rejected。

  1. function Promise(fn) {
  2. //...
  3. function resolve(value) {
  4. //...
  5. _this.status = 'resolved';
  6. }
  7. function reject(err) {
  8. //...
  9. _this.status = 'rejected';
  10. }
  11. }

状态凝固

如果状态从 pending 变为 rejected 或者 resolved,那么状态就会凝固,不会再改变。

只需要在 rej 和 res 函数执行时,增加判断即可,如果是 pending 才执行,如果状态已经改变为别的,就什么都不做。

防止多次调用 res 和 rej 函数。

  1. function Promise(fn) {
  2. //...
  3. function resolve(value) {
  4. //=> 只有在 pending 状态才会执行相关操作
  5. if (_this.status === 'pending') {
  6. //...
  7. }
  8. }
  9. function reject(err) {
  10. //=> 只有在 pending 状态才会执行相关操作
  11. if (_this.status === 'pending') {
  12. //...
  13. }
  14. }
  15. }

then 方法

  • then 方法需要传入两个回调函数
  • 如果状态为 resolve,执行成功回调
  • 如果状态为 reject,执行失败回调
  • 如果只传递一个或没有传参,做容错处理
Promise.prototype.then = function then(success, fail) {
  switch (this.status) {
    case 'resolved':
      success && success(this.value);
      break;
    case 'rejected':
      fail && fail(this.err);
      break;
  }
}

异步处理

要使得在异步操作执行完成之后,再执行相应的回调函数,也就是在 res 或者 rej 执行的时候(导致状态修改的情况下),才去执行相应的回调。

这时,需要借助发布订阅模式的思想,来处理异步是否完成。

function Promise(fn) {
  //...
  //=> 存储成功和失败的回调,利用了订阅发布模式思想,这里建立事件池
  this.resCallbacks = [];
  this.rejCallbacks = [];

  function resolve(value) {
    //=> 只有在 pending 状态才会执行相关操作
    if (_this.status === 'pending') {
      //...
      //=> 执行相应事件池中的所有事件,相当于 发布
      // 每个事件函数执行传入相应的参数
      _this.resCallbacks.forEach(item => {
        item && item(_this.value);
      });
    }
  }

  function reject(err) {
    //=> 只有在 pending 状态才会执行相关操作
    if (_this.status === 'pending') {
      //...
      _this.rejCallbacks.forEach(item => {
        item && item(_this.err);
      })
    }
  }
  //...
}

在 then 中,同步操作执行完毕,没有执行 res 或者 rej,其状态还是 pending,所以需要在状态为 pending 时,进行处理,在事件池中添加相应的事件。

Promise.prototype.then = function then(success, fail) {
  switch (this.status) {
    //...
    case 'pending':
      // 处理异步操作
      this.resCallbacks.push(success);
      this.rejCallbacks.push(fail);
  }
}

链式操作处理

想要实现链式的操作,那么 then 就必须返回一个 Promise 实例

代码

//=> 接受一个回调函数
function Promise(fn) {
  //=> 初始状态
  let _this = this;
  this.status = 'pending';

  //=> 传给成功和失败函数参数的初始值
  this.value = undefined;
  this.err = undefined;

  //=> 存储成功和失败的回调,利用了订阅发布模式思想
  this.resCallbacks = [];
  this.rejCallbacks = [];

  function resolve(value) {
    //=> 只有在 pending 状态才会执行相关操作
    if (_this.status === 'pending') {
      _this.status = 'resolved';
      _this.value = value;
      _this.resCallbacks.forEach(item => {
        item && item(_this.value);
      });
    }
  }

  function reject(err) {
    //=> 只有在 pending 状态才会执行相关操作
    if (_this.status === 'pending') {
      _this.status = 'rejected';
      _this.err = err;
      _this.rejCallbacks.forEach(item => {
        item && item(_this.err);
      })
    }
  }

  //=> 创建实例时,立即执行 fn
  try {
    fn(resolve, reject);
  } catch (e) {
    // 如果执行报错,则直接执行失败回调
    reject(e);
  }
}

Promise.prototype.then = function then(success, fail) {
  switch (this.status) {
    case 'resolved':
      success && success(this.value);
      break;
    case 'rejected':
      fail && fail(this.err);
      break;
    case 'pending':
      // 处理异步操作
      this.resCallbacks.push(success);
      this.rejCallbacks.push(fail);
  }
}

let p = new Promise(function (res, rej) {
  setTimeout(() => {
    res(11);
  }, 1000);
  console.log(1234);
}).then((data) => {
  console.log(data);
  }, (err) => {
  console.log(err);
});