实现一个完整Promise对象

613 阅读18分钟

前言

Promise 对象,相信学前端的小伙伴们都对它很熟了,它是一种现在前端异步编程的主流形式,关于它的文章从基础使用到底层原理分析、实现网上是一抓一大把,它也是现在前端面试的高频题目,深度掌握它也成为一个衡量前端能力的标准了。

之前也写过一篇关于 Promise 对象的文章(一文,一定能让你手动实现Promise),不过那会写得比较简单,这次算是来给它补全(来还债了(ー_ー)!!),废话不多说,下面马上就来开始本文的愉快之旅。

这次所写的 Promise 对象是以 Promises/A+中文规范)为标准的,ES6 中就是采用了 Promise/A+ 规范。

Promise 的规范有很多,如 Promise/APromise/BPromise/D 以及 Promise/A 的升级版 Promise/A+Promise/A+ 并未规范 catchallrace 等这些方法,这些方法都是 ES6 自己规范的,其实也不难,后面也会一一讲解到。

源码实现

本章尝试以例子的形式来写出整个 Promise 对象的源码,每行代码会备注详细的注解不怕看不懂哦。

基础结构

  • 我们以下面基本使用的例子先来引出 Promise 对象的整体结构。
let p = new Promise((resolve, reject) => {
  console.log('Start')
  resolve('橙某人');
})
p.then(r1 => {
  console.log(r1)
}, e1 => {
  console.log(e1)  
})
console.log('End');

image.png

通过上面 Promise 对象的基本使用,我们能先定义出它的构造函数、.then(onFulfilled, onRejected) 方法以及还有两个回调函数 resolve()reject() 的定义,当然,它们是形参,名字你可以随便定义。

// 定义构造函数
function myPromise(executor) {
  let _this = this; // 保留 this 的指向,防止下面使用过程中 this 指向不明
  _this.status = 'pending'; // status有三种状态: pending || fulfilled || rejected
  _this.result = undefined; // 保存调用 resolve(result) 方法传递的值
  _this.reason = undefined; // 保存调用 reject(reason) 方法传递的值
  
  // 定义 resolve() 
  function resolve(result) {
    if(_this.status === 'pending') {
      _this.result = result; // 保存成功的值
      _this.status = 'fulfilled'; // 把 Promise 对象的状态改为成功
    }
  }
  // 定义 reject()
  function reject(reason) {
    if(_this.status === 'pending') {
      _this.status = 'rejected'; // 保存失败的值
      _this.reason = reason; // 把 Promise 对象的状态改为失败
    }
  }
  // 立即执行 executor(), 执行 executor() 过程可能会出错, 所以要严谨进行try/catch一下, 并且错误结果能给 Promise 捕获
  try{
    executor(resolve, reject);
  }catch(err) {
    reject(err)
  }
}
// 定义 .then() 方法, 它的参数是可选的, 只要不是函数就忽略即可
myPromise.prototype.then = function (onFulfilled, onRejected) {
  if(this.status === 'fulfilled' && typeof onFulfilled === 'function') {
    // .then() 调用是一个异步过程
    setTimeout(() => {
      onFulfilled(this.result);
    })
  }
  if(this.status === 'rejected' && typeof onRejected === 'function') {
    setTimeout(() => {
      onRejected(this.reason);
    })
  }
}

一个 Promise 会有三种状态,分别是 pending/fulfilled/rejected ,可以简单理解为等待中、成功和失败三种状态,这是基本知识了,就不作过多介绍了。整体过程不是很难,唯一需要注意的是 .then() 方法是一个异步方法,所以我们把它们放在 setTimeout 中去执行。

  • 下面我们来调整一下上面的例子:
let p = new Promise((resolve, reject) => {
  console.log('Start')
  setTimeout(() => {
    resolve('橙某人');
  })
})
p.then(r1 => {
  console.log(r1)
}, e1 => {
  console.log(e1)  
})
console.log('End');

我们异步调用了 resolve('橙某人'); 但它的运行结果和上面的一致,来看看如何实现这个异步调用过程。

function myPromise(executor) {
  ...
  _this.onFulfilledCbs = []; // 调用 then(onFulfilled, onRejected) 方法时, 如果 status 为 pending 状态, 说明 resolve() 为异步调用, 所以 Promise 状态还没改变, 则先将 onFulfilled 回调函数存储起来
  _this.onRejectedCbs = []; // 调用 then(onFulfilled, onRejected) 方法时, 如果 status 为 pending 状态, 说明 reject() 为异步调用, 所以 Promise 状态还没改变, 则先将 onRejected 回调函数存储起来
  
  function resolve(result) {
    if(_this.status === 'pending') {
      _this.result = result;
      _this.status = 'fulfilled';
      // 执行所有 then(onFulfilled, onRejected) 方法的 onFulfilled
      while (_this.onFulfilledCbs.length) {
        _this.onFulfilledCbs.shift()(); // 按 then() 方法的调用顺序执行并删除
      }
    }
  }
  function reject(reason) {
    if(_this.status === 'pending') {
      _this.reason = reason;
      _this.status = 'rejected';
      // 执行所有 then(onFulfilled, onRejected) 方法的 onRejected
      while (_this.onRejectedCbs.length) {
        _this.onRejectedCbs.shift()(); // 按 then() 方法的调用顺序执行并删除
      }
    }
  }
  ...
}
myPromise.prototype.then = function (onFulfilled, onRejected) {
  ...
  // Promise 对象还是 pending 状态, 则说明 resolve() 或者 reject() 进行了异步调用
  if(this.status === 'pending') {
    if(typeof onFulfilled === 'function') {
      /**
       * 之所以 push 一个函数, 主要是为了回调函数能传值.
       * 也可以直接 push 函数, 传值交由上面来完成: 
       * this.onFulfilledCbs.push(onFulfilled);
       * _this.onFulfilledCbs.shift()(_this.result)
       */
      this.onFulfilledCbs.push(() => {
        onFulfilled(this.result);
      })  
    }
    if(typeof onRejected === 'function') {
      this.onRejectedCbs.push(() => {
        onRejected(this.reason);
      })  
    }  
  }
}

其实异步调用 resolve() 或者 reject().then() 方法会比它们俩先执行,那么 .then() 内部使用的 Promise 对象的状态会是 pending 等待状态,那么我们只能先把 .then(onFulfilled, onRejected) 方法的成功回调和失败回调先存起来,只有当真正调用过 resolve() 或者 reject() 再去把它们拿出来执行即可。

then() 方法

Promise 对象的基本结构不是很难,也比较好理解,它最强大的地方是它支持链式调用,这是最复杂也是最重点的内容,接下来我们来研究研究它的链式调用,上重头戏了。

链式调用

我们一样先看例子:

let p = new Promise((resolve, reject) => {
  console.log('Start')
  setTimeout(() => {
    resolve('橙某人');
  })
})
p.then(r1 => {
  return r1;
}).then(r2 => {
  return r2;
}).then(r3 => {
  console.log(r3); // 橙某人
})
console.log('End');

由例子上看,我们可以知道每次调用完 then() 方法必然会返回一个 Promise 对象,它才能接下来继续链式调用下去。而且, then() 中能使用 return 把结果继续往后传递下去,基于这两点我们再来改造下 then() 方法。

myPromise.prototype.then = function (onFulfilled, onRejected) {
  // 返回一个新的 Promise 对象
  return new myPromise((resolve, reject) => {
    if(this.status === 'fulfilled' && typeof onFulfilled === 'function') {
      setTimeout(() => {
        // 执行 onFulfilled or onRejected 方法可能会出现错误, 所以try/catch一下,并把错误往下传递
        try{
          // 获取到 return 的值
          let x = onFulfilled(this.result);
          // 调用新的 Promise 对象的 resolve() 把值继续传下去
          resolve(x);
        }catch(e) {
          reject(e);
        }
      })
    }
    if(this.status === 'rejected' && typeof onRejected === 'function') {
      setTimeout(() => {
        try{  
          let x = onRejected(this.reason);
          reject(x);
        }catch(e) {
          reject(e);
        }
      })
    }
    if(this.status === 'pending') {
      if(typeof onFulfilled === 'function') {
        this.onFulfilledCbs.push(() => {
          try{  
            let x = onFulfilled(this.result);
            resolve(x);
          }catch(e) {
            reject(e);
          }
        })  
      }
      if(typeof onRejected === 'function') {
        this.onRejectedCbs.push(() => {
          try{ 
            let x = onRejected(this.reason);
            reject(x);
          }catch(e) {
            reject(e);
          }
        })  
      }  
    }
  })
}

通过在 then() 中返回一个新的 Promise 对象,就能实现无限链式调用下去了,是不是挺简单;而通过执行 onFulfilled() 或者 onRejected() 方法来获取return 的值,也就是上面代码中的 x ;最后我们通过新的 Promise 对象的 resolve(x)reject(x) 方法就能把值继续往下传啦(-^〇^-)。当然执行 onFulfilled() 或者 onRejected() 可能会出现错误,所以我们都加上了 try/catch 来捕获一下错误。

值穿透

然后,你以为这就完了吗?还没呢,我们再来看个例子:

let p = new myPromise((resolve, reject) => {
  console.log('Start')
  setTimeout(() => {
    resolve('橙某人');
  })
})
p
.then()
.then()
.then()
.then(r4 => {
  console.log(r4); // 橙某人
})
console.log('End');

是不是这种“值穿透”传递也挺神奇的?我们来看看它要如何去实现。

myPromise.prototype.then = function (onFulfilled, onRejected) {
  // 解决值穿透
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : result => result;
  onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}; // onRejected 直接用 throw 抛出错误,这样它会被 try/catch 捕获到; 注意 onRejected 中的错误不能以 return 的形式传递,都是通过 throw 往后抛下去
  // 返回一个新的 Promise 对象
  ...
}

其实这个过程就是一个初始化默认值过程,也就是 .then() 会变成 .then(result => result),前面我们说过对于 then() 方法的两个参数 onFulfilledonRejected 我们只考虑它们是函数的情况。现在,当它们不是函数的时候,我们就给它们默认值一个函数并且把结果继续往后抛,就能实现所谓的值穿透了。

值类型判断

接下来就是 then() 方法的最后一步了,这一步也挺复杂的,需要对 then(onFulfilled, onRejected) 两个参数 return 的值进行验证,也就是对 x 做一些类型检验。按照 Promises/A+ 标准规范的信息,我们需要对 x 做这以下几个校验:

image.png

53c9a8f7fa6821ab08162a7e7d309dd.jpg

呃......是不是都看懵逼了,这是图灵社区译的中文文档,应该算是很贴近了,你也可以去看看英语文档,其实应该也差不多,一般官方文档的话语都是那么难以理解的。不过,不用着急,我把测试例子写出来,你可能就好理解很多了。

  1. x 与 then() 新返回的 Promise 相等。
let p = new Promise((resolve, reject) => {
  console.log('Start')
  resolve('橙某人');
})
let t = p.then(() => {
  return t
})
console.log('End');

image.png

  1. x 为另一个 Promise 对象。 新的 Promise 对象状态处于 fulfilled 或者 rejected 状态。
let p = new Promise((resolve, reject) => {
  console.log('Start')
  resolve('橙某人');
});
let pNew = new Promise((resolve, reject) => {
  resolve('yd');
  // reject('yd');
});
p.then(() => {
  return pNew
}).then(r => {
  console.log(r);
});
console.log('End');

image.png

新的 Promise 对象状态处于 pending 状态。

let p = new Promise((resolve, reject) => {
  console.log('Start')
  resolve('橙某人');
})
let pNew = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('yd'); // 1秒后才调用, 让 Promise 处于 pending 状态
  }, 1000)
});
p.then(() => {
  return pNew
}).then(r => {
  console.log(r); // 一秒后才输出
});
console.log('End');

image.png

第二个测试的两个小例子,结果虽然一样,但第一个的结果是控制台立即就输出了 yd,但第二个的结果是控制台等了一秒后才输出 yd

  1. 如果 x 为对象或者函数。
let p = new Promise((resolve, reject) => {
  console.log('Start')
  resolve('橙某人');
})
// x 为对象
p.then(() => {
  return {
    then: function(resolve, reject) {
      console.log('我是对象的then属性', this);
      resolve('yd')
    }
  }
}).then(r => {
  console.log(r); // yd
}, e => {
  console.log(e)
})
// x 为函数
function fn() {}
fn.then = function(resolve, reject) {
  console.log('我是对函数的then属性', this);
  resolve('yd')
}
p.then(() => {
  return fn;
}).then(r => {
  console.log(r); // yd
})

console.log('End');  

image.png

这样子是不是有点像是把对象和函数的 then 属性造了一个新的 Promise 对象呢?这也告诉我们取名字的时候要注意哦,千万不要和关键字有关联(^ω^)。

下面我们先来看看 then() 方法的调整:

myPromise.prototype.then = function (onFulfilled, onRejected) {
  ...
  var p = new myPromise((resolve, reject) => {
    if(this.status === 'fulfilled' && typeof onFulfilled === 'function') {
      setTimeout(() => {
        let x = onFulfilled(this.result);
        // 借助 resolvePromise() 统一验证 x 
        resolvePromise(p, x, resolve, reject);
      })
    }
    if(this.status === 'rejected' && typeof onRejected === 'function') {
      setTimeout(() => {
        let x = onRejected(this.reason);
        resolvePromise(p, x, resolve, reject);
      })
    }
    if(this.status === 'pending') {
      if(typeof onFulfilled === 'function') {
        this.onFulfilledCbs.push(() => {
          let x = onFulfilled(this.result);
          resolvePromise(p, x, resolve, reject);
        })  
      }
      if(typeof onRejected === 'function') {
        this.onRejectedCbs.push(() => {
          let x = onRejected(this.reason);
          resolvePromise(p, x, resolve, reject);
        })  
      }  
    }
  })
  return p;
}

因为对 x 验证的部分比较复杂和重复,所以另外写一个 resolvePromise() 方法。

/**
 * 验证 x 的类型
 * @param {*} p: 新返回的 Promise 对象
 * @param {*} x: onFulfilled or onRejected 返回的值
 * @param {*} resolve: 新 Promise 对象的 resolve 方法
 * @param {*} reject: 新 Promise 对象的 reject 方法
 * @returns 
 */
function resolvePromise(p, x, resolve, reject) {
  // 1. 如果 x 为新返回的 Promise 对象, 则直接抛出错误
  if (x != undefined && p === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }

  // 2. 如果 x 为另一个 myPromise 对象, 也就是显式 return new myPromise() 
  if (x instanceof myPromise) {
    if (x.status === 'pending') { // 如果 x 这个 myPromise 对象处于  pending 状态, 则要等待直 x 被执行或拒绝
      x.then(r => {
        resolvePromise(p, r, resolve, reject); // 递归等待 x 被执行
      }, e => {
        reject(e);
      })
    } else { // 如果 x 这个 myPromise 对象已经处于  fulfilled or rejected 状态, 则直接再次调用 then() 方法!
      x.then(resolve, reject); // 这里的意思其实可以理解为, 我们执行了新的 myPromise 对象的 then() 方法, 它的两个回调参数, 就是我们下一个 then() 两个回调的参数. 
    }
    return;
  }

  // 3. 如果 x 为对象或者函数, 如果不是就是普通的数据类型, 直接通过
  var called; // 只调用一次, 防止重复调用
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    // 如果 x.then 为一个函数, 则我们会执行这个函数, 并且函数内部的 this 指向 x, 执行这个函数内部可能会有错误, 所以需要 try/catch 一下
    try {
      let then = x.then; // 获取 x 身上的 then() 方法
      if (typeof then === 'function') {
        then.call(x, function (res) {
          if (called) return;
          called = true;
          // 可能 res 还是一个 promise 对象, 所以需要递归下去判断 res(也就是x) 的类型
          resolvePromise(p, res, resolve, reject); // 递归
        }, function (err) {
          if (called) return;
          called = true;
          reject(err)
        })
      } else {
        resolve(x); // 普通值, x.then 可能是 x.then = '橙某人
      }
    } catch (e) {
      reject(e)
    }
  } else {
    resolve(x); // 普通值
    // 这里为什么直接就调用 resolve() 不考虑 reject(), 因为规定错误不能通过 return 来传递, 只能通过 throw 的形式, 也就是 onRejected 中 return 是无效的
  }
}

resolvePromise() 方法是比较复杂的,从上面文字的介绍你就应该能感觉到,它里面涉及到了递归的概念,我觉得看懂它的最好方式就是跟着例子慢慢去分析,加油骚年,有什么疑问也欢迎你留言给我,必回。

静态方法

熬过最难的一部分,剩下会简单一些了,我们继续往下瞧。

catch

catch 方法比较常用了,主要用于捕获各种异常,它也支持链式调用,我们先来瞧瞧这些例子。

// 例子一
let p = new Promise((resolve, reject) => {
  resolve('橙某人');
})
p
.then()
.catch()
.then(r => {
  console.log(r); // 橙某人
})

// 例子二
let p = new Promise((resolve, reject) => {
  reject('出错');
})
p
.then(()=>{}, e => {
  console.log(e); // 出错
})

// 例子三
let p = new Promise((resolve, reject) => {
  reject('出错');
})
p
.then()
.catch(e => {
  console.log(e); // 出错
})

// 例子四
let p = new Promise((resolve, reject) => {
  resolve('橙某人');
})
p
.then(() => {
  console.log(a)
}, e => {
  console.log(e); // ReferenceError: a is not defined  
})

// 例子五
let p = new Promise((resolve, reject) => {
  resolve('橙某人');
})
p
.then(() => {
  console.log(a)
})
.catch(e => {
  console.log(e); // ReferenceError: a is not defined
})

由上面的例子我们也知道 catch 方法会返回一个 Promise 对象来完成链式调用,而且它的功能和 then(onFulfilled, onRejected) 方法中的 onRejected 基本是一致的。我们来瞧瞧它的源码样子如何:

myPromise.prototype.catch = function(onRejected) {
  // 相当于再次调用了一次 then() 方法, 把传进来的参数作为了 then() 方法的第二个参数
  return this.then(null, onRejected);
}

resolve

Promise.resolve() 方法是 Promise 对象的静态方法,可不要把它也挂载到原型上去了哦,它的作用是返回一个 fulfilled 状态的新 Promise 对象。

// 例子一
let p1 = Promise.resolve('橙某人1');
p1.then(r1 => {
  console.log(r1); // 橙某人1
})
// 等同
let p11 = new Promise((resolve, reject) => {
  resolve('橙某人1');
});

// 例子二
let p2 = Promise.resolve(p1);
console.log(p2 === p1); // true
p2.then(r2 => {
  console.log(r2); // 橙某人1
})

看例子一,你会发现 Promise.resolve() 像是一个简写形式,看例子二,当我们传递的值为一个 Promise 对象的时候,就会返回这个 Promise 对象,这点和我们的原来的 resolve() 方法可有点区别。我们先来看看源码的实现情况:

myPromise.resolve = function(result) {
  // 如果传递的值为一个 Promise 对象, 则直接返回该对象, 否则返回一个 `fulfilled` 状态的新 `Promise` 对象
  return result instanceof myPromise ? result : new myPromise(resolve => resolve(result));
}

看完源码的实现,我们再回过头来想想原来 resolve() 方法的问题,我们先跑个例子先:

// Promise
let p1 = new Promise((resolve, reject) => {
  console.log(resolve('橙某人')); // undefined
})
let p2 = new Promise((resolve, reject) => {
  console.log(resolve(p1)); // undefined
})
p1.then(r1 => {
  console.log(r1); // 橙某人
})
p2.then(r2 => {
  console.log(r2); // 橙某人
})
// myPromise
let p1 = new myPromise((resolve, reject) => {
  console.log(resolve('橙某人')); // undefined
})
let p2 = new myPromise((resolve, reject) => {
  console.log(resolve(p1)); // undefined
})
p1.then(r1 => {
  console.log(r1); // 橙某人
})
p2.then(r2 => {
  console.log(r2); // myPromise {...}
})

对比官方的 Promise 和我们自己的 myPromise 对象的输出,就能知道我们写的 resolve() 方法还是存在点问题,返回的结果都是 undefined 这点就没啥区别了,但是对于传入的值还要验证一下。

function myPromise(executor) {
    ...
    function resolve(result) {
    // 如果传递的 result 是一个 Promise 对象, 则需要返回这个 Promise 对象, 并且取出这个 Promise 对象的值, 继续传递下去
    if(result instanceof myPromise) {
      result.then(resolve, reject);
      return;
    }
    if (_this.status === 'pending') {
      ...
    }
  }
  ...
}

reject

Promise.reject 方法也是 Promise 对象的静态方法,和上面的 Promise.resolve 差不多,但它不用考虑验证参数这一步,我们直接看源码:

myPromise.reject = function(reason) {
  return new myPromise((resolve, reject) => reject(reason));
}

all

Promise.all 方法也是 Promise 对象的静态方法,比较常见了,经常应用于开发并发请求的功能中。它接收一个 Promise 对象的数组,如果数组项是普通值,则内部也会把它包装成一个 Promise 对象,并返回一个新的 Promise 对象;新返回的 Promise 对象会等待数组全部项都进入 fulfilled 状态,它才会进入 fulfilled 状态。我们先看看它的使用:

// 例子一
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('橙某人1');
  }, 1000)
})
let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('橙某人2');
  }, 2000)
})
let p = Promise.all([p1, 1, p2]);
p.then(r => {
  console.log(r); // ["橙某人1", 1, "橙某人2"]
}).catch(e => {
  console.log(e)
})
// 例子二
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('橙某人1');
  }, 1000)
})
let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('出错');
  }, 2000)
})
let p = Promise.all([p1, 1, p2]);
p.then(r => {
  console.log(r);
}).catch(e => {
  console.log(e); // 出错
})

源码实现过程:

myPromise.all = function(promiseArr) {
  if(!Array.isArray(promiseArr)) return console.error('Parameter requires an Array');
  let result = [];
  let count = 0;
  // 返回一个新的 Promise 对象
  return new myPromise((resolve, reject) => {
    promiseArr.forEach(p => {
      // 把数组每项都传入 myPromise.resolve 方法中, 如果是 Promise 对象则直接返回, 如果不是 Promise 对象则会被包装成 Promise 对象返回
      myPromise.resolve(p).then(r => {
        result.push(r);
        count++;
        if(count === promiseArr.length) {
          resolve(result);
        }
      }, e => {
        reject(e); // 只要一个失败就把 Promise 对象直接改成失败状态
      })
    })
  })
}

race

Promise.racePromise.all 一样接收一个 Promise 对象的数组,也返回一个新的 Promise 对象,一旦数组中的某个项进入 fulfilled 或者 rejected 状态,新 Promise 对象的也会相应进入该状态。它基本和 Promise.all 差不多就不多讲了,直接上源码:

myPromise.race = function(promiseArr) {
  if(!Array.isArray(promiseArr)) return console.error('Parameter requires an Array');
  return new myPromise((resolve, reject) => {
    promiseArr.forEach(p => {
      myPromise.resolve(p).then(r => {
        resolve(r);
      }, e => {
        reject(e); 
      })
    })
  })
}

完整源码

// 定义构造函数
function myPromise(executor) {
  var _this = this;
  _this.status = 'pending'; // status有三种状态: pending || fulfilled || rejected
  _this.result = undefined; // 保存调用 resolve(result) 方法传递的值
  _this.reason = undefined; // 保存调用 reject(reason) 方法传递的值
  _this.onFulfilledCbs = []; // 调用 then(onFulfilled, onRejected) 方法时, 如果 status 为 pending 状态, 说明 resolve() 为异步调用, 所以 Promise 状态还没改变, 则先将 onFulfilled 回调函数存储起来
  _this.onRejectedCbs = []; // 调用 then(onFulfilled, onRejected) 方法时, 如果 status 为 pending 状态, 说明 reject() 为异步调用, 所以 Promise 状态还没改变, 则先将 onRejected 回调函数存储起来

  // 定义 reject()
  function resolve(result) {
    // 如果传递的 result 是一个 Promise 对象, 则需要返回这个 Promise 对象, 并且取出这个 Promise 对象的值, 继续传递下去
    if(result instanceof myPromise) {
      result.then(resolve, reject);
      return;
    }
    if (_this.status === 'pending') {
      _this.result = result;
      _this.status = 'fulfilled';
      // 执行所有 then(fulfillCbs, rejectedCbs) 方法的 onFulfilled 
      while (_this.onFulfilledCbs.length) {
        _this.onFulfilledCbs.shift()(); // 按 then() 方法的调用顺序执行并删除
      }
    }
  }
  // 定义 reject()
  function reject(reason) {
    if (_this.status === 'pending') {
      _this.reason = reason;
      _this.status = 'rejected';
      // 执行所有 then() 方法的 onRejected 
      while (_this.onRejectedCbs.length) {
        _this.onRejectedCbs.shift()(); // 按 then() 方法的调用顺序执行并删除
      }
    }
  }
  // 立即执行 executor(), 执行 executor() 过程可能会出错, 所以要严谨进行try/catch一下, 并且错误结果能给 Promise 捕获
  try {
    executor(resolve, reject);
  } catch (err) {
    reject(err)
  }
}

/**
 * then() 方法的参数都是可选的
 * @param {*} onFulfilled 
 * @param {*} onRejected 
 * @returns 
 */
myPromise.prototype.then = function (onFulfilled, onRejected) {
  // 解决值穿透
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : result => result;
  onRejected = typeof onRejected === 'function' ? onRejected : reason => {
    throw reason
  };
  // 返回一个新的 Promise 对象
  var p = new myPromise((resolve, reject) => {
    if (this.status === 'fulfilled' && typeof onFulfilled === 'function') {
      // .then 调用是一个异步过程
      setTimeout(() => {
        try {
          let x = onFulfilled(this.result);
          // 借助 resolvePromise() 统一验证 x 
          resolvePromise(p, x, resolve, reject);
        } catch (e) {
          reject(e)
        }
      })
    }
    if (this.status === 'rejected' && typeof onRejected === 'function') {
      setTimeout(() => {
        try {
          let x = onRejected(this.reason);
          resolvePromise(p, x, resolve, reject);
        } catch (e) {
          reject(e)
        }
      })
    }
    if (this.status === 'pending') {
      if (typeof onFulfilled === 'function') {
        /**
         * 之所以 push 一个函数, 主要是为了回调函数能传值.
         * 也可以直接 push 函数, 传值交由上面来完成: 
         * this.onFulfilledCbs.push(onFulfilled);
         * _this.onFulfilledCbs.shift()(_this.result)
         */
        this.onFulfilledCbs.push(() => {
          try {
            let x = onFulfilled(this.result);
            resolvePromise(p, x, resolve, reject);
          } catch (e) {
            reject(e)
          }
        })
      }
      if (typeof onRejected === 'function') {
        this.onRejectedCbs.push(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(p, x, resolve, reject);
          } catch (e) {
            reject(e)
          }
        })
      }
    }
  })
  return p;
}

/**
 * 验证 x 的类型
 * @param {*} p: 新返回的 Promise 对象
 * @param {*} x: onFulfilled or onRejected 返回的值
 * @param {*} resolve: 新 Promise 对象的 resolve 方法
 * @param {*} reject: 新 Promise 对象的 reject 方法
 * @returns 
 */
function resolvePromise(p, x, resolve, reject) {
  // 1. 如果 x 为新返回的 Promise 对象, 则直接抛出错误
  if (x != undefined && p === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }

  // 2. 如果 x 为另一个 myPromise 对象, 也就是显式 return new myPromise() 
  if (x instanceof myPromise) {
    if (x.status === 'pending') { // 如果 x 这个 myPromise 对象处于  pending 状态, 则要等待直 x 被执行或拒绝
      x.then(r => {
        resolvePromise(p, r, resolve, reject); // 递归等待 x 被执行
      }, e => {
        reject(e);
      })
    } else { // 如果 x 这个 myPromise 对象已经处于  fulfilled or rejected 状态, 则直接再次调用 then() 方法!
      x.then(resolve, reject); // 这里的意思其实可以理解为, 我们执行了新的 myPromise 对象的 then() 方法, 它的两个回调参数, 就是我们下一个 then() 两个回调的参数. 
    }
    return;
  }

  // 3. 如果 x 为对象或者函数, 如果不是就是普通的数据类型, 直接通过
  var called; // 只调用一次, 防止重复调用
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    // 如果 x.then 为一个函数, 则我们会执行这个函数, 并且函数内部的 this 指向 x, 执行这个函数内部可能会有错误, 所以需要 try/catch 一下
    try {
      let then = x.then; // 获取 x 身上的 then() 方法
      if (typeof then === 'function') {
        then.call(x, function (res) {
          if (called) return;
          called = true;
          // 可能 res 还是一个 promise 对象, 所以需要递归下去判断 res(也就是x) 的类型
          resolvePromise(p, res, resolve, reject); // 递归
        }, function (err) {
          if (called) return;
          called = true;
          reject(err)
        })
      } else {
        resolve(x); // 普通值, x.then 可能是 x.then = '橙某人
      }
    } catch (e) {
      reject(e)
    }
  } else {
    resolve(x); // 普通值
    // 这里为什么直接就调用 resolve() 不考虑 reject(), 因为规定错误不能通过 return 来传递, 只能通过 throw 的形式, 也就是 onRejected 中 return 是无效的
  }
}

/**
 * 捕获异常, 本质是 onRejected 参数
 * @param {*} onRejected 
 * @returns 
 */
myPromise.prototype.catch = function (onRejected) {
  // 相当于再次调用了一次 then() 方法, 把传进来的参数作为了 then() 方法的第二个参数
  return this.then(null, onRejected)
}

/**
 * 返回一个 `fulfilled` 状态的新 `Promise` 对象。
 * @param {*} result 
 * @returns 
 */
myPromise.resolve = function(result) {
  // 如果传递的值为一个 Promise 对象, 则直接返回该对象, 否则返回一个 `fulfilled` 状态的新 `Promise` 对象
  return result instanceof myPromise ? result : new myPromise(resolve => resolve(result));
}

/**
 * 返回一个 `rejected` 状态的新 `Promise` 对象。
 * @param {*} result 
 * @returns 
 */
myPromise.reject = function(reason) {
  return new myPromise((resolve, reject) => reject(reason));
}

myPromise.all = function(promiseArr) {
  if(!Array.isArray(promiseArr)) return console.error('Parameter requires an Array');
  let result = [];
  let count = 0;
  // 返回一个新的 Promise 对象
  return new myPromise((resolve, reject) => {
    promiseArr.forEach(p => {
      // 把数组每项都传入 myPromise.resolve 方法中, 如果是 Promise 对象则直接返回, 如果不是 Promise 对象则会被包装成 Promise 对象返回
      myPromise.resolve(p).then(r => {
        result.push(r);
        count++;
        if(count === promiseArr.length) {
          resolve(result);
        }
      }, e => {
        reject(e); // 只要一个失败就把 Promise 对象直接改成失败状态
      })
    })
  })
}

myPromise.race = function(promiseArr) {
  if(!Array.isArray(promiseArr)) return console.error('Parameter requires an Array');
  return new myPromise((resolve, reject) => {
    promiseArr.forEach(p => {
      myPromise.resolve(p).then(r => {
        resolve(r);
      }, e => {
        reject(e); 
      })
    })
  })
}



至此,本篇文章就写完啦,撒花撒花。

image.png

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。