由浅入深实现一个Promise

503 阅读18分钟

myPromise

由浅入深的实现一个属于自己的Promise,并且实现Promise的大部分功能

1、执行器

原版

首先我们由浅入深的了解一下Promise的使用。

image-20220427155804937

  1. 我们会传给 Promise 一个函数,函数会被立即执行,这个函数我把它叫做执行器。
  2. 会返回一个 Promise对象 包含状态(State) 和 结果(result)

模仿

编写 myPromise.js

难度不大 我就解释了

const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态

class myPromise {
  constructor(executor) {
    executor();
  }
  // 当前的状态
  state = PENDING;
  // 结果值
  result = undefined;
}

测试

image-20220427155945215

ok,到此我们Promise最简单的一个功能我们已经实现

2、执行器参数和 then 方法

针对上面,我们继续加深一下Promise的使用。

原版

image-20220427160638164

  1. 执行器会接收两个参数,resolvereject
  2. 在执行器中执行了 resolvereject 会改变当前对象的状态,resolve方法会把状态变成成功(fulfilled),reject方法会把状态变成失败(rejected)
  3. 已经改变状态的Promise对象的状态不能再改变。就是说多次调用 resolvereject 只会执行第一次调用的那个方法
  4. promise对象有一个then方法,会接收两个参数,第一个参数是成功的回调,第二个参数是失败回调
    • 在成功回调函数中,接收成功的值,也就是再执行器中调用resolve方法传递的值
    • 在失败回调函数中,接收失败的值,也就是再执行器中调用reject方法传递的值

模仿

编写 myPromise.js

  1. 我们先定义一个resolve 方法,先判断当前的状态,只有等待状态才会继续,紧接着我们修改当前的状态是成功的并且保存成功值
  2. reject 方法同上
  3. 接着我们需要在执行器中,传入我们自己的resolvereject,这里需要注意一下,真正调用resolvereject的时候是在使用者调用的环境,比如浏览器,但是resolve做的事情是要对当前对象进行处理,所以用bind处理一下this指向
  4. 接着实现then方法,判断当前的状态,执行对应的回调函数
const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态

class myPromise {
  constructor(executor) {
    executor(this.resolve.bind(this), this.reject.bind(this));
  }
  // 当前的状态
  state = PENDING;
  // 结果值
  result = undefined;

  resolve(val) {
    // 状态不是 pending 就直接返回
    if (this.state !== PENDING) return;
    // 修改状态为成功状态
    this.state = FULFILLED;
    // 绑定成功结果
    this.result = val;
  }

  reject(val) {
    // 状态不是 pending 就直接返回
    if (this.state !== PENDING) return;
    // 修改状态为失败状态
    this.state = REJECTED;
    // 绑定失败结果
    this.result = val;
  }

  then(successCb, failCb) {
    if (this.state === FULFILLED) {
      successCb(this.result);
    } else if (this.state === REJECTED) {
      failCb(this.result);
    }
  }
}

测试

image-20220427162856504

这个时候,myPromise基本就成型了,紧接着还有许许多多的问题需要我们处理,一步一步来吧。

3、执行器中的异步代码

看一下上面的例子可以发现,我在写原版的时候是用了1ms的定时器模拟了网络请求,但是在myPromise中并没有写定时器。因为我们现在的myPromse还不支持在执行器中有异步代码。

想一下,代码是同步执行的,执行器执行的时候,resolvereject是延迟的,那再执行到下面then方法的时候,当前myPromise的状态还是等待状态,就不会执行成功或者失败回调了

如何来改造呢?

myPromise中的then方法里面,判断一下当前的状态,如果是等待的状态,我们就要想办法把传入的成功/失败回调函数,要放在 resolve/reject执行。

ok,思路有了,改造myPromise代码。

  1. then方法中,如果当前状态是等待状态,就需要把 成功回调或者失败回调保存起来
  2. myPromise类中定义两个变量用来保存then方法的成功回调和失败回调
  3. resolve/reject方法中,最后判断一下有没有成功/失败回调,有的话就需要调用一下
const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态

class myPromise {
  constructor(executor) {
    executor(this.resolve.bind(this), this.reject.bind(this));
  }
  // 当前的状态
  state = PENDING;
  // 结果值
  result = undefined;
  // 成功回调
  successCallback = undefined;
  // 失败回调
  failCallback = undefined;

  resolve(val) {
    // 状态不是 pending 就直接返回
    if (this.state !== PENDING) return;
    // 修改状态为成功状态
    this.state = FULFILLED;
    // 绑定成功结果
    this.result = val;

    // 判断成功回调是否存在 如果存在 调用
    this.successCallback && this.successCallback(this.result);
  }

  reject(val) {
    // 状态不是 pending 就直接返回
    if (this.state !== PENDING) return;
    // 修改状态为失败状态
    this.state = REJECTED;
    // 绑定失败结果
    this.result = val;

    // 判断失败回调是否存在 如果存在 调用
    this.failCallback && this.failCallback(this.result);
  }

  then(successCb, failCb) {
    if (this.state === FULFILLED) {
      successCb(this.result);
    } else if (this.state === REJECTED) {
      failCb(this.result);
    } else {
      // 当前状态是 等待状态,那么成功或者失败的回调要再 resolve/reject之后执行
      this.successCallback = successCb;
      this.failCallback = failCb;
    }
  }
}

4、多次 then

promisethen方法是可以被多次调用的。

原版

image-20220427165744500

模仿

目前,如果执行器中的代码是同步的,myPromise也一样是可以实现多次then的,如果执行器中存在了异步的代码,因为我们再then方法中对等待状态的回调函数做的是一个赋值操作,那么多次的then的话,目前的情况下我们只会执行最后一个then方法,因为前面的都被覆盖了。

如何来改造呢?

我们现在需要改造存储then方法的回调函数的地方,之前是直接赋值,现在需要实现多次then能保存下来每一次then的回调函数,在 resolve/reject执行的时候也需要修改,之前是执行回调函数,现在需要按顺序执行回调函数。

编写 myPromise.js

  1. 修改存储成功/失败回调函数的地方,让他默认值是一个数组,这样就可以缓存多个回调函数了
  2. then方法接触到等待状态的时候,把成功/失败回调函数存到数组里面
  3. resolve/reject的时候,在回调函数数组中,从头取出每一个回调函数并且执行
const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态

class myPromise {
  constructor(executor) {
    executor(this.resolve.bind(this), this.reject.bind(this));
  }
  // 当前的状态
  state = PENDING;
  // 结果值
  result = undefined;
  // 成功回调
  successCallbackList = [];
  // 失败回调
  failCallbackList = [];

  resolve(val) {
    // 状态不是 pending 就直接返回
    if (this.state !== PENDING) return;
    // 修改状态为成功状态
    this.state = FULFILLED;
    // 绑定成功结果
    this.result = val;

    // 多次的then需要按顺序执行成功的回调函数
    while (this.successCallbackList.length) this.successCallbackList.shift()(this.result);
  }

  reject(val) {
    // 状态不是 pending 就直接返回
    if (this.state !== PENDING) return;
    // 修改状态为失败状态
    this.state = REJECTED;
    // 绑定失败结果
    this.result = val;

    // 多次的then需要按顺序执行失败的回调函数
    while (this.failCallbackList.length) this.failCallbackList.shift()(this.result);
  }

  then(successCb, failCb) {
    if (this.state === FULFILLED) {
      successCb(this.result);
    } else if (this.state === REJECTED) {
      failCb(this.result);
    } else {
      // 当前状态是 等待状态,那么成功或者失败的回调要再 resolve/reject之后执行
      this.successCallbackList.push(successCb);
      this.failCallbackList.push(failCb);
    }
  }
}

测试

image-20220427172222018

5、微任务

原版

promisethen方法中的回调函数是被加入到了微任务队列中的,执行的顺序会晚于主程序。

image-20220427181130695

模仿

我们现在执行器代码中因为有同步和异步的区别,在then中判断了当前myPromise的状态,进行了分别的处理。在同步的情况下,代码会立即执行对应的回调函数。

异步的情况下,代码是丢到了回调函数队列中,在resolve或reject中执行的。

所以我们其实这里都没有用到微任务,都需要做一个微任务。

那么,我们现在可以在then中不判断状态,直接把回调函数丢到回调函数队列中,在resolve或reject中遍历回调函数队列并且执行的这个过程我们可以丢到微任务队列中。

编写 myPromise.js

const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态

class myPromise {
  constructor(executor) {
    executor(this.resolve.bind(this), this.reject.bind(this));
  }
  // 当前的状态
  state = PENDING;
  // 结果值
  result = undefined;
  // 成功回调
  successCallbackList = [];
  // 失败回调
  failCallbackList = [];

  resolve(val) {
    // 状态不是 pending 就直接返回
    if (this.state !== PENDING) return;
    // 修改状态为成功状态
    this.state = FULFILLED;
    // 绑定成功结果
    this.result = val;

    this._nextTick(() => {
      // 多次的then需要按顺序执行成功的回调函数
      while (this.successCallbackList.length) this.successCallbackList.shift()(this.result);
    });
  }

  reject(val) {
    // 状态不是 pending 就直接返回
    if (this.state !== PENDING) return;
    // 修改状态为失败状态
    this.state = REJECTED;
    // 绑定失败结果
    this.result = val;

    this._nextTick(() => {
      // 多次的then需要按顺序执行失败的回调函数
      while (this.failCallbackList.length) this.failCallbackList.shift()(this.result);
    });
  }

  then(successCb, failCb) {
    this.successCallbackList.push(successCb);
    this.failCallbackList.push(failCb);
  }

  // Promise -> MutationObserver -> setImmediate -> setTimeout
  _nextTick(cb) {
    // 执行回调函数
    const p = Promise.resolve();
    p.then(cb);
    // -------
    // MutationObserver 实现微任务
    // let ob = new MutationObserver(() => {
    //   cb();
    //   // 用完销毁
    //   ob.disconnect();
    //   ob = null;
    // });
    // ob.observe(document.body, { attributes: true });
    // document.body.setAttribute('_yl', Math.random());
    // -------
    // setTimeout 实现宏任务
    // setTimeout(cb, 0);
  }
}

  1. resolve或reject执行的时候把执行回调函数队列的过程丢到了 _nextTick 方法中
  2. _nextTick 的实现借鉴了 vue,主要就是判断当前环境下,Promise -> MutationObserver -> setImmediate -> setTimeout这些变量存不存在,优先级按照箭头顺序,哪个存在用哪个对象实现异步。
  3. then方法现在不管三七二十一直接把回调函数丢到队列里面了,为什么现在可以这样做,之前的时候要去判断myPromise的状态然后还要根据不同的状态做不同的事情?
    • 因为遍历回调函数队列的过程是丢到了异步任务中,我们之前在写代码 .then进行注册事件的这个过程是同步过程,现在then的回调函数一定会先被注册,这个异步任务就能保证在遍历回调函数队列的时候,then的方法一定已经被注册了。
    • 或者说,之前不能这样写的原因是,执行 同步resolve的时候,then还没有被执行,那么resolve方法遍历回调函数队列的时候就遍历了空数组了。现在遍历的过程变成了异步了,then方法会先执行,遍历回调函数队列后执行,回调函数队列就一定是有值的。

6、链式 then

原版

image-20220427173407066

  1. promisethen方法是支持链式调用的
  2. 后面then的回调函数中的参数是上一次then中成功回调的返回值。
    • 情况一:如果then中成功回调返回的是一个普通值,下一个then接收的就是这个普通值
    • 情况二:如果then中成功回调返回的是一个myPromise对象,下一个then接收的就是这个myPromise对象resolve的结果
  3. 如果前面的promise变成失败状态了,在then的失败回调函数中会被捕获到,那么下一个then的参数会使用失败回调的返回值

模仿

首先我们要明确,then链式调用的原理是 then方法需要返回一个myPriomise对象,为了好记,我们给返回的对象叫做thenPromise,改造一下then方法

then(successCb, failCb) {
  const thenPromise = new myPromise((thenResolve, thenReject) => {
    this.successCallbackList.push(successCb);
    this.failCallbackList.push(failCb);
  });
  return thenPromise;
}

然后我们需要清楚,链式调用中,后面的then拿到的参数是哪来的,不难理解,后面的then的参数需要我们这个thenPromise执行thenResolve方法。

这里我们还需要清楚一点,successCb这个函数是我们自定义的没问题,那这个函数干了什么,返回了什么都是自定义的,但是这个函数的参数是myPromise提供的,是在myPromise中执行resolve方法的时候传入的当前myPromiseresult

返回普通对象

  1. 针对情况一,前面then方法的成功回调返回的是一个普通值,那么我们只需要拿到successCb的返回值并且 thenResolve 出来就可以。代码如下
then(successCb, failCb) {
  const thenPromise = new myPromise((thenResolve, thenReject) => {
    this.successCallbackList.push((thenResult) => {
      // 执行成功回调函数,拿到返回值
      thenResult = successCb(thenResult);
      // resolve出这个值,链式调用的时候后面的then就可以拿到了
      thenResolve(thenResult);
    });

    this.failCallbackList.push((thenResult) => {
      // 执行失败回调函数,拿到返回值
      thenResult = failCb(thenResult);
      // 失败函数的返回值是被下一个then成功回调接收的  这里需要执行thenResolve
      thenResolve(thenResult);
    });
  });
  return thenPromise;
}

返回myPromise对象

  1. 针对情况二,前面then方法的成功回调返回的是一个myPromise对象,也就是说我们执行successCb拿到的返回值thenResult是一个myPromise对象,那么链式调用的时候,后面的then拿到的值应该是 thenResult这个myPromise对象的 result结果,也就是说我们返回的thenPromise 需要拿到thenResult这个myPromise对象的result
    • 这里逻辑很绕,希望能认真的分析一下
    • thenResult 是一个myPromise对象
    • 我们返回的thenPromise 需要拿到thenResult 成功状态的result
    • 拿到的意思也就是说thenResolve方法要获取到thenResult 成功状态的result
    • 获取thenResult 成功状态的result可以让thenResult 调用一下then方法,这个then方法可以接收到result,这个时候我们在直接thenResolve一下就可以了

ok 逻辑清楚了就上代码

then(successCb, failCb) {
  const thenPromise = new myPromise((thenResolve, thenReject) => {
    this.successCallbackList.push((thenResult) => {
      // 执行成功回调函数,拿到返回值
      thenResult = successCb(thenResult);
	  // 如果thenResult 是一个myPromise对象
      if (thenResult instanceof myPromise) {
        // thenResolve 需要拿到thenResult这个myPromise对象的result
        return thenResult.then(
          (thenResultPromiseResult) => thenResolve(thenResultPromiseResult),
          (thenResultPromiseResult) => thenReject(thenResultPromiseResult)
        );
      }

      // resolve出这个值,链式调用的时候后面的then就可以拿到了
      thenResolve(thenResult);
    });

    this.failCallbackList.push((thenResult) => {
      // 执行失败回调函数,拿到返回值
      thenResult = failCb(thenResult);
      // 如果thenResult 是一个myPromise对象
      if (thenResult instanceof myPromise) {
        // thenResolve 需要拿到thenResult这个myPromise对象的result
        return thenResult.then(
          (thenResultPromiseResult) => thenResolve(thenResultPromiseResult),
          (thenResultPromiseResult) => thenReject(thenResultPromiseResult)
        );
      }

      // 失败函数的返回值是被下一个then成功回调接收的  这里需要执行thenResolve
      thenResolve(thenResult);
    });
  });
  return thenPromise;
}

看到这里,我希望你现在没有啥疑问了在接着往下看,因为这里确实很绕。

写道这里,代码其实可以优化一下

function test(val){ return Math.ceil(val); }
// 如果一个方法 直接返回了一个方法的调用,参数一致的情况下
test === Math.ceil  // 等式成立

// 那么
(thenResultPromiseResult) => thenResolve(thenResultPromiseResult) === thenResolve // 等式成立

所以我们的代码可以简化一下

then(successCb, failCb) {
  const thenPromise = new myPromise((thenResolve, thenReject) => {
    this.successCallbackList.push((thenResult) => {
      // 执行成功回调函数,拿到返回值
      thenResult = successCb(thenResult);

      // 如果thenResult 是一个myPromise对象
      if (thenResult instanceof myPromise) {
        // thenResolve 需要拿到thenResult这个myPromise对象的result
        return thenResult.then(thenResolve, thenReject);
      }

      // resolve出这个值,链式调用的时候后面的then就可以拿到了
      thenResolve(thenResult);
    });

    this.failCallbackList.push((thenResult) => {
      // 执行失败回调函数,拿到返回值
      thenResult = failCb(thenResult);

      // 如果thenResult 是一个myPromise对象
      if (thenResult instanceof myPromise) {
        // thenResolve 需要拿到thenResult这个myPromise对象的result
        return thenResult.then(thenResolve, thenReject);
      }

      // 失败函数的返回值是被下一个then成功回调接收的  这里需要执行thenResolve
      thenResolve(thenResult);
    });
  });
  return thenPromise;
}

这里做一个小解释,在情况二的时候我们写了return进行返回了一个值,其实这个值是无意义的,我们链式调用关注的是then方法的返回值,我们并不关注传给thensuccessCb方法的返回值,写return的主要目的是防止代码继续向下执行而报错

测试

  • 测试返回普通值

image-20220428141025041

  • 测试返回myPromise

image-20220428141122004

7、异常捕获

发生异常的地方应该是两个,一个是执行器执行的时候可能会异常,一个是传给then的两个函数执行可能异常。

如果捕获到了异常,我们直接调用reject方法就可以了

constructor 修改

constructor(executor) {
  try {
    executor(this.resolve.bind(this), this.reject.bind(this));
  } catch (e) {
    this.reject(e);
  }
}

then 修改

then(successCb, failCb) {
  const thenPromise = new myPromise((thenResolve, thenReject) => {
    this.successCallbackList.push((thenResult) => {
      try {
        // 执行成功回调函数,拿到返回值
        thenResult = successCb(thenResult);
        // 如果thenResult 是一个myPromise对象
        if (thenResult instanceof myPromise) {
          // thenResolve 需要拿到thenResult这个myPromise对象的result
          return thenResult.then(thenResolve, thenReject);
        }
        // resolve出这个值,链式调用的时候后面的then就可以拿到了
        thenResolve(thenResult);
      } catch (e) {
        thenReject(e);
      }
    });

    this.failCallbackList.push((thenResult) => {
      try {
        // 执行失败回调函数,拿到返回值
        thenResult = failCb(thenResult);
        // 如果thenResult 是一个myPromise对象
        if (thenResult instanceof myPromise) {
          // thenResolve 需要拿到thenResult这个myPromise对象的result
          return thenResult.then(thenResolve, thenReject);
        }
        // 失败函数的返回值是被下一个then成功回调接收的  这里需要执行thenResolve
        thenResolve(thenResult);
      } catch (e) {
        thenReject(e);
      }
    });
  });
  return thenPromise;
}

测试

  • 测试执行器异常

image-20220428143821729

  • 测试链式调用then异常

image-20220428144333607

7.5、小小的撒花

至此 myPromise的核心部分已经实现的差不多了,小小的撒花一下

8、then参数优化

原版

image-20220428144829822

promisethen如果不传参数的话是不会执行的,会将状态传递下去

模仿

链式调用已经实现了,现在要做没有参数的时候的 状态传递。

上面的代码我们换一种思路理解一下

.then().then()

// 等价交换

.then(val => val).then(val => val)

这样写是不是思路就很清楚了,那么我们接下来改造一下then方法

代码如下

then(successCb, failCb) {
  // 参数处理
  successCb = successCb ? successCb : (result) => result;
  failCb = failCb ? failCb : (result) => {throw result};
    
  ......
}

需要注意的是失败的情况下,我们需要把失败状态的myPromiseresult用异常给抛出来

测试

image-20220428145907498

9、静态all方法实现

原版

all如何使用我就不演示了,不会的可以看下面的测试用例

模仿

all方法有几个要注意的点

  1. 传入的数组中的所有的 myPromise对象都resolveall方法才可以resolve
  2. all方法then接收到的结果的顺序 需要和 传递数组中的顺序保持一致
static all(array) {
  return new myPromise((resolve, reject) => {
    const arrayLen = array.length; // 数组的长度
    const res = []; //返回结果
    let fulfilledNum = 0; //成功的个数

    function addData(index, value) {
      // 顺序一致需要采用索引赋值而不是push
      res[index] = value;
      // 记录成功的个数 只有全部成功了 all 才 resolve
      fulfilledNum += 1;
      if (fulfilledNum === arrayLen) {
        resolve(res);
      }
    }
    array.forEach((item, index) => {
      if (item instanceof myPromise) {
        // myPromise 对象
        item.then(
          (val) => addData(index, val),
          (err) => reject(err)
        );
      } else {
        // 其他情况
        addData(index, item);
      }
    });
  });
}

测试

image-20220428152758660

10、静态resolve方法实现

模仿

  1. 传入普通值就返回一个成功状态 并且结果是 传入值的 myPromise
  2. 传入的是 myPromise对象,则直接返回传入值
static resolve(value) {
  if (value instanceof myPromise) return value;
  return new myPromise((resolve) => resolve(value));
}

测试

image-20220428153956250

11、静态reject方法实现

模仿

参考上面的 我就不贴写测试过程了

static reject(err) {
  if (err instanceof myPromise) return err;
  return new myPromise((resolve, reject) => reject(err));
}

12、finally实例方法实现

  1. 链式调用的时候,finally 会返回一个 promise ,这个promise并不是一个新的promise,而是返回了自己
  2. finally 传入一个函数,不管自己的状态是什么都会执行
  3. finally的回调函数的返回值并不会影响到后面的then,但是如果返回了一个promise对象的话,会等待整个promise对象状态改变之后再继续执行后面的then,而且只是等待返回的promise的状态变化,并不会影响后面的then

第一版

finally(cb) {
  return this.then(
    // value 是 自己的resolve执行的时候传过来的自己的结果
    (value) => {cb(); return value},
    (err) =>  {cb(); return {throw err}},
  );
}

这一版考虑到了情况一和二,但是没有考虑到 cb函数的返回值是一个 promise对象时候的情况

第二版

finally(cb) {
  return this.then(
    // 包了一层myPromise.resolve 是为了等待cb函数返回的promise状态改变之后再返回 自己的value
    (value) => myPromise.resolve(cb()).then(() => value),
    (err) => myPromise.resolve(cb()).then(() => {throw err })
  );
}

13、实现catch

catch的实现等价于一个有失败回调函数没有成功回调函数的then方法

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

完结撒花-最终代码

const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态

class myPromise {
  constructor(executor) {
    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (e) {
      this.reject(e);
    }
  }
  // 当前的状态
  state = PENDING;
  // 结果值
  result = undefined;
  // 成功回调
  successCallbackList = [];
  // 失败回调
  failCallbackList = [];

  resolve(val) {
    // 状态不是 pending 就直接返回
    if (this.state !== PENDING) return;
    // 修改状态为成功状态
    this.state = FULFILLED;
    // 绑定成功结果
    this.result = val;

    this._nextTick(() => {
      // 多次的then需要按顺序执行成功的回调函数
      while (this.successCallbackList.length) this.successCallbackList.shift()(this.result);
    });
  }

  reject(val) {
    // 状态不是 pending 就直接返回
    if (this.state !== PENDING) return;
    // 修改状态为失败状态
    this.state = REJECTED;
    // 绑定失败结果
    this.result = val;

    this._nextTick(() => {
      // 多次的then需要按顺序执行失败的回调函数
      while (this.failCallbackList.length) this.failCallbackList.shift()(this.result);
    });
  }

  then(successCb, failCb) {
    // 参数处理
    successCb = successCb ? successCb : (value) => value;
    failCb = failCb
      ? failCb
      : (errStr) => {
          throw errStr;
        };
    const thenPromise = new myPromise((thenResolve, thenReject) => {
      this.successCallbackList.push((thenResult) => {
        try {
          // 执行成功回调函数,拿到返回值
          thenResult = successCb(thenResult);
          // 如果thenResult 是一个myPromise对象
          if (thenResult instanceof myPromise) {
            // thenResolve 需要拿到thenResult这个myPromise对象的result
            return thenResult.then(thenResolve, thenReject);
          }
          // resolve出这个值,链式调用的时候后面的then就可以拿到了
          thenResolve(thenResult);
        } catch (e) {
          thenReject(e);
        }
      });

      this.failCallbackList.push((thenResult) => {
        try {
          // 执行失败回调函数,拿到返回值
          thenResult = failCb(thenResult);
          // 如果thenResult 是一个myPromise对象
          if (thenResult instanceof myPromise) {
            // thenResolve 需要拿到thenResult这个myPromise对象的result
            return thenResult.then(thenResolve, thenReject);
          }
          // 失败函数的返回值是被下一个then成功回调接收的  这里需要执行thenResolve
          thenResolve(thenResult);
        } catch (e) {
          thenReject(e);
        }
      });
    });
    return thenPromise;
  }

  static all(array) {
    return new myPromise((resolve, reject) => {
      const arrayLen = array.length; // 数组的长度
      const res = []; //返回结果
      let fulfilledNum = 0; //成功的个数

      function addData(index, value) {
        // 顺序一致需要采用索引赋值而不是push
        res[index] = value;
        // 记录成功的个数 只有全部成功了 all 才 resolve
        fulfilledNum += 1;
        if (fulfilledNum === arrayLen) {
          resolve(res);
        }
      }
      array.forEach((item, index) => {
        if (item instanceof myPromise) {
          // myPromise 对象
          item.then(
            (val) => addData(index, val),
            (err) => reject(err)
          );
        } else {
          // 其他情况
          addData(index, item);
        }
      });
    });
  }

  finally(cb) {
    return this.then(
      // value 是 自己的resolve执行的时候传过来的自己的结果
      (value) => myPromise.resolve(cb()).then(() => value),
      // 包了一层myPromise.resolve 是为了等待cb函数返回的promise状态改变之后再返回 自己的value
      (err) =>
        myPromise.resolve(cb()).then(() => {
          throw err;
        })
    );
  }

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

  static resolve(value) {
    if (value instanceof myPromise) return value;
    return new myPromise((resolve) => resolve(value));
  }

  static reject(err) {
    if (err instanceof myPromise) return err;
    return new myPromise((resolve, reject) => reject(err));
  }

  // Promise -> MutationObserver -> setImmediate -> setTimeout
  _nextTick(cb) {
    // 执行回调函数
    const p = Promise.resolve();
    p.then(cb);
    // -------
    // MutationObserver 实现微任务
    // let ob = new MutationObserver(() => {
    //   cb();
    //   // 用完销毁
    //   ob.disconnect();
    //   ob = null;
    // });
    // ob.observe(document.body, { attributes: true });
    // document.body.setAttribute('_yl', Math.random());
    // -------
    // setTimeout 实现宏任务
    // setTimeout(cb, 0);
  }
}