手撸Promise心路历程

1,605 阅读18分钟

第一步 实现一个简单的Promise

  1. Promise是一个类 如何创建一个Promise对象呢? 我们需要通过new 关键字创建。很简单吧,所以我们知道Promise是一个类
class Promise {

}

new Promise()
  1. 执行器
  • 需求分析: 这个函数为Promise的执行器,创建这个类的时候执行器会”立即“执行。

  • 代码设计 这样我们应该给Promise类在构造器里面传递一个执行器,并立即执行。

class Promise {
  // Promise创建立即执行执行器
  constructor(executor) {
    // Promise创建立即执行执行器
    executor()
  }
}


new Promise(executor)
  1. Promise 状态
  • 需求分析: Promise一共有三种状态,分别为成功(fulfilled),失败(rejected), 等待(pending) Promise的状态, 一旦状态确定就不可修改

    只能pending --> fulfilled

    只能pending ---> rejected

  • 代码设计: Promise 就需要 一个属性status(状态值)和两个改变状态的函数resolve 和 reject

并且执行器接收两个参数resolve和reject,这两个函数使用来改变状态的

执行resolve函数将Promise状态改为 fulfilled 执行reject函数将Promise改为 rejected

如果status值不是pending 阻止程序向下执行

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

class MyPromise {
  constructor(executor) {
    // Promise创建立即执行执行器
    executor(this.resolve, this.reject)
  }
  status = PENDING
  // 为什么使用箭头函数,是为了resolve 和 reject里面的this指向的是 这个Promise对象
  // 为什么使用常量不直接写字符串 1. 编辑器会有提示,2. 可以复用
  resolve = () => {
    // 如果状态不是pending 阻止程序向下执行
    if(this.status !== PENDING ) return;
    // 将状态更改为成功
    this.status = FULFILLED
  }

  reject = () => {
    // 如果状态不是pending 阻止程序向下执行
    if(this.status !== PENDING ) return;
    // 将状态更改为失败
    this.status = REJECTED
  }
}

new Promise((resolve, reject) => {
  resolve('')// => fulfilled
  // reject() => rejected
})
  1. then函数
  • 需求分析

    1. then内部做得事情就是判断Promise状态:
    • 如果状态是成功,调用成功的回调函数
    • 如果状态是失败,调用失败回调函数
    1. then函数是定义在原型上的函数
  • 代码设计

then函数需要两个参数,成功回调 successCallback 失败回调 failCallback,

... // 定义常量那些代码 省略
class MyPromise {
  ...// 上面那些代码省略
  then(successCallback, failCallback) {
    // 判断状态
    if(this.status === FULFILLED) {
      successCallback();
    } else if(this.status === REJECTED) {
      failCallback();
    }
  }
}
  1. then 成功回调有一个参数,表示成功之后的值, then失败回调有一个参数,表示失败之后的原因
  • 需求设计
  1. 当调用resolve的时候,将成功的值给Promise,
  2. 当调用reject的时候,将失败的原因给Promise
  3. 这些值在通过Promise对象给then的成功回调 和失败回调
  • 代码设计 resolve函数接收成功值,存储到Promise对象里的value reject函数接收失败的原因,存储到Promise对象里的reason

then的成功回调获取 this.value,失败回调获取this.reason

...
class MyPromise {
  ...
  // value reason属于Promise实力
  // 成功之后的值
  value = void 0;
  // 失败后的原因
  reason = void 0;
  resolve = () => {
    ... // 省略两行改变状态代码
    // 保存成功后的值
    this.value = value
  }

  reject = () => {
    ... // 省略两行改变状态代码
    // 保存失败后的原因
    this.reason = reason
  }
  then(successCallback, failCallback) {
    // 判断状态
    if(this.status === FULFILLED) {
      successCallback(this.value);
    } else if(this.status === REJECTED) {
      failCallback(this.reason);
    }
  }
}

第二步: 在 Promise类中加入异步逻辑

刚刚我们实现的简单的Promise的时候,是没有考虑到异步情况的,接下来我们将Promise类加入异步代码。

const MyPromise = require('./MyPromise')
let promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功')
  }, 2000)
  // reject('失败')
})
promise.then(value => console.log(value), reason => console.log(reason))

问题一: 当执行器里面出现异步代码,我们如何让resolve('成功')两秒之后执行,保证then的成功回调能够拿到值value === "成功"而不是undefined 分析:当我们创建Promise对象的时候,执行器会马上执行。而执行器里面有异步代码,这时候主线程不会执行异步里面代码而是继续往下执行,这时候then代码就会马上执行,但是resolve这个函数会在两秒后执行,所以then执行之后 value拿到的是undefined。 解决:

  • Promise是有一个状态维护的,当resolve函数没有执行,status还是为 pending,所提在then的时候我们判断status状态为pending的时候,我们不执行回调,应该把回调先保存起来,给Promise类添加两个属性: 成功回调失败回调
  • 然后在resolve 和 reject 函数中判断 Promise的 成功回调 successCallback 和 失败回调 failCallback是否有值,有则执行相应的回调

这样我们就会根据 resolve 或者 reject 改变状态之后 在这两个方法里面执行 当status为pending状态的then里面存储的成功回调或者失败回调

class MyPromise {
  ...
  // 成功回调
  successCallback = void 0;
  // 失败回调
  failCallback = void 0;
  ...
  resolve = value => {
    ...
    // 判断成功是否存在 如果存在 调用
    this.successCallback && this.successCallback(this.value)
  }

  reject = reason => {
    ...
    // 判断成功是否存在 如果存在 调用
    this.failCallback && this.failCallback(this.reason)
  }
  // Promise的原型
  then(successCallback, failCallback) {
    // 判断状态
    if(this.status === FULFILLED) {
      successCallback(this.value);
    } else if(this.status === REJECTED) {
      failCallback(this.reason);
    } else {
      // 状态为等待
      // 将成功回调和失败回调存储起来
      this.successCallback = successCallback;
      this.failCallback = failCallback;
    }
  }
}

第三步: then 方法加强

  1. 实现then方法多次调用,且添加多个处理函数
  • 需求分析 同一个Promise的多个then方法呢是可以多次被执行的,且then里面回调方法都是会被执行,如下代码foo1 foo2都会执行
promise.then(foo1)
promise.then(foo2)
promise.then...

Promise执行器有两种情况有可能是同步也有可能是异步的:

  • 同步处理 如果是同步则resolve或者reject会马上执行,就会知道状态是成功或者失败 then里面回调可以马上执行,不需要做特殊处理
  • 异步处理 如果是异步的时候,需要进行特殊处理,将每一个then里面的回调都应该存储起来,当状态发生改变的时候,才执行对应的then的回调

代码实现难点:

  • 将每个then的回调都存储起来, 我们前面的 successCallback 和 failCallback 都是普通值。现在我们应该使用数组将他们存储起来。
  • 将Promise类的实例属性 successCallback 和 failCallback都改为数组,每次then调用的时候将回调push进去
  • 然后在状态变化后 遍历执行回调
class MyPromise {
  ...
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];
  // 为什么使用箭头函数,是为了resolve 和 reject里面的this指向的是 这个Promise对象
  // 为什么使用常量不直接写字符串 1. 编辑器会有提示,2. 可以复用

  resolve = value => {
    ...
    // 遍历所有then里面的成功回调
    while(this.successCallback) this.successCallback.shift()(this.value)
  }

  reject = reason => {
    ...
    // 遍历所有then里面的失败回调
    while(this.failCallback.length) this.failCallback.shift()(this.reason)
  }
  // Promise的原型
  then(successCallback, failCallback) {
    // 判断状态
    if(this.status === FULFILLED) {
      successCallback(this.value);
    } else if(this.status === REJECTED) {
      failCallback(this.reason);
    } else {
      // 状态为等待
      // 把每次then里面成功回调和失败回调存储起来
      this.successCallback.push(successCallback);
      this.failCallback.push(failCallback);
    }
  }
}
  1. then的链式调用
  • 需求分析

    • promise的then方法是可以链式调用的:
    • 后面一个then方法的值拿到的是前一个then方法返回的值
    promise.then(value => {
      console.log(value)
      return 100;
    }).then(value => {
      // 这个值应该是上一个then返回的值 为100
      console.log(value) // 打印出来是100
    })
    
  • 代码设计

    • 如果要实现链式调用,那then返回的应该都是一个promise的对象,所以在then里面我们首先创建一个promise对象,完成第一步
    • 我们创建的promise对象需要一个执行器,刚好可以把then里面的立即执行的代码放入执行器中即可
    then(successCallback, failCallback) {
      let promise2 = new MyPromise(() => {
        // 判断状态
        if(this.status === FULFILLED) {
          successCallback(this.value);
        } else if(this.status === REJECTED) {
          failCallback(this.reason);
        } else {
          // 状态为等待
          // 把每次then里面成功回调和失败回调存储起来
          this.successCallback.push(successCallback);
          this.failCallback.push(failCallback);
        }
      });
      return promise2;
    }
    

    接下来我们想怎么把上一个then的回调返回值返回给下一个then

    • then成功(失败)回调的时候,都会有一个返回值,我们先声明一个变量将他们保存起来
    • 接下来只要把这个值给返回promise对象执行器的resolve这个函数,就传递到下个then方法里了
    then(successCallback, failCallback) {
      let promise2 = new MyPromise((resolve, reject) => {
        // 判断状态
        if(this.status === FULFILLED) {
          // 存储 当前then成功回调返回的值
          let x = successCallback(this.value);
          // 将这个值返回给下一个promise对象
          resolve(x)
        } else if(this.status === REJECTED) {
          failCallback(this.reason);
        } else {
          // 状态为等待
          // 把每次then里面成功回调和失败回调存储起来
          this.successCallback.push(successCallback);
          this.failCallback.push(failCallback);
        }
      });
      return promise2
    }
    

    回调返回来的值有两种情况:

    • 普通值:可以直接调用resolve
    • promise对象:查看promise对象返回的结果,再根据promise对象返回的结果 决定调用resolve 还是调用reject

    由于上面的逻辑在状态等待,成功 和 失败三个状态都需要处理上面的逻辑。所以将这个公共逻辑抽象成一个方法是比较合适的

    ...
    then(successCallback, failCallback) {
      let promise2 = new MyPromise((resolve, reject) => {
        // 判断状态
        if(this.status === FULFILLED) {
          let x = successCallback(this.value);
          // resolve(x)
          // 判断x的值是普通值还是promise对象
          // 如果是普通值 直接调用resolve
          // 如果是promise对象 查看promise对象的返回结果
          // 在根据promise对象返回的结果 决定调用reslove 还是reject
          resolvePromise(x, resolve, reject)
        } else if(this.status === REJECTED) {
          let y = failCallback(this.reason);
        } else {
          // 状态为等待
          // 把每次then里面成功回调和失败回调存储起来
          this.successCallback.push(successCallback);
          this.failCallback.push(failCallback);
        }
      });
      return promise2
    }
    // 公共函数
    function resolvePromise(x, resolve, reject) {
      if(x instanceof MyPromise) {
        // promise 对象
        // x.then(value => resolve(value), reason => reject(reason));
        x.then(resolve, reject);
      } else {
        // 普通值
        resolve(x);
      }
    }
    
  1. then方法链式调用识别promise对象自返回

上面我们已经知道,then方法是可以返回promise对象的,但是有一种错误情况就是,then方法返回自己promise对象这种行为是不允许的, 因为这样会发生循环调用。看代码

let promise = new Promise((resolve, reject) => {
  resolve('成功')
}).then(value => {
  console.log(value)
  // 返回自己的话,就会发生循环调用了
  return promise
})
// 成功
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

我们应该处理这个情况呢?

其实也很简单,then方法里面回调返回来的promise对象和当前then生成的对象是同一个对象的话 就应该执行reject 函数返回

then(successCallback, failCallback) {
  let promise2 = new MyPromise((resolve, reject) => {
    // 判断状态
    if(this.status === FULFILLED) {
      let x = successCallback(this.value);
      // 这里将马上返回的promise2对象传给resolvePromise函数进行判断
      resolvePromise(promise2, x, resolve, reject)
    } else if(this.status === REJECTED) {
      failCallback(this.reason);
    } else {
      this.successCallback.push(successCallback);
      this.failCallback.push(failCallback);
    }
  });
  return promise2
}
function resolvePromise(promise2, x, resolve, reject) {
  // 判断当前then 准备返回的 promise对象和上次then成功回调的x promise对象是同一个的话 代码停止向下执行并且抛出异常reject
  if(promise2 === x) {
    return reject(new TypeError('Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>'))
  }
  if(x instanceof MyPromise) {
    // promise 对象
    // x.then(value => resolve(value), reason => reject(reason));
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

但是如果就这样写程序还是会报错的,then方法里面 promise2是拿不到的 因为new了之后promise2才有,但是promise2又是在创建promise对象执行器里面使用。所以使用setTimeout 方法将此段代码异步执行即可

then(successCallback, failCallback) {
  let promise2 = new MyPromise((resolve, reject) => {
    if(this.status === FULFILLED) {
      // 这里使用这个这段代码就会异步 在生成promise2对象之后再执行 真泥马巧妙
      setTimeout(() => {
        let x = successCallback(this.value);
        resolvePromise(promise2, x, resolve, reject)
      },0)
    } else if(this.status === REJECTED) {
      failCallback(this.reason);
    } else {
      this.successCallback.push(successCallback);
      this.failCallback.push(failCallback);
    }
  });
  return promise2
}
  1. 捕获错误及then链式调用的其他状态的补充

为了程序的健壮性我们还是有必要去捕获错误处理错误的。

  1. 第一个错误处理,执行器错误的捕获。当执行器里面的代码出现错误的时候我们应当捕获错误,并且执行reject将捕获的错误返回
class MyPromise {
  constructor(executor) {
    // 捕获并处理执行器里面的错误
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  ...
}
  1. 如果成功回调里面的代码出现错误,也应该捕获,捕获之后返回reject状态并将捕获的错误返回
then(successCallback, failCallback) {
    let promise2 = new MyPromise((resolve, reject) => {
      if(this.status === FULFILLED) {
        setTimeout(() => {
          // 捕获then回调的错误并处理
          try {
            let x = successCallback(this.value);
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        },0)
      }
      ...
    });
    return promise2
  }
  1. 把失败回调和异步回调也捕获错误
  • 失败回调捕获错误
then(successCallback, failCallback) {
    let promise2 = new MyPromise((resolve, reject) => {
      if(this.status === FULFILLED) {
        setTimeout(() => {
          // 捕获then回调的错误并处理
          try {
            let x = failCallback(this.value);
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        },0)
      }
      ...
    });
    return promise2
  }
  • 异步处理是将 成功回调和失败回调存储在数组中等到执行器执行的时候再运行回调 before:

eles {
  this.successCallback.push(this.successCallback);
  this.failCallback.push(this.failCallback);
}

如果直接将成功回调和失败回调存储数组了,就无法捕获到了所以,我们修改一下,存入数组一个匿名函数 after:

else {
  // 状态为等待
  // 把每次then里面成功回调和失败回调存储起来
  this.successCallback.push(() => {
    setTimeout(() => {
      // 捕获then回调的错误并处理
      try {
        let x = successCallback(this.value);
        resolvePromise(promise2, x, resolve, reject)
      } catch (error) {
        reject(error)
      }
    },0)
  });
  this.failCallback.push(() => {
    setTimeout(() => {
      // 捕获then回调的错误并处理
      try {
        let x = failCallback(this.reason);;
        resolvePromise(promise2, x, resolve, reject)
      } catch (error) {
        reject(error)
      }
    },0)
  });
}

这样就可以地捕获到异步成功和失败回调代码的异常。

  1. 将then方法的参数变成可选参数 需求分析:
  • then方法的两个参数实际上都是可选参数,就是说我们一个参数都可以不传,那这样的情况下promise的流程会是什么样的呢。
var promise = new Promise((resolve, reject) => {resolve(100)})
promise
  .then()
  .then()
  .then(console.log)
  • 我们发现上面代码打印的是100,那应该怎么去实现这样的功能呢?

  • 当我们不传递任何参数then()的时候实际上是then(value => value) 形参是value,直接将value返回, 就会在无参的时候传递下去。

代码设计:

  • 在then方法内部我们要判断then有没有参数,如果没有参数我们就补一个value => value这样的参数,这样的话状态就会向后依次传递了
  // Promise的原型
  then(successCallback, failCallback) {
    // 判断是否有成功回调,如果没有则给一个默认函数 value => value
    successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => {throw reason}
    let promise2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if(this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        },0)
    ...
    return promise2
  }

第四步: Promise 其他Api

1. Promise.all 方法的实现

all 是用来处理异步并发问题的。

  • 允许我们异步调用的顺序得到异步代码执行的结果.
  • 接收一个数组作为参数,数组里面可以是任何值普通值也可以是一个promise对象,这个数组值得顺序也是我们得到结果的顺序
  • 返回值也是promise对象,所以all方法也可以做then链式调用
  • all方法里面所有promise对象所有都是成功的那这个all就是成功的,如果有一个失败则all返回的promise对象也是失败的
function p2 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {resolve('p2')}, 2000)
  })
}
function p3 () {
  return new Promise((resolve, reject) => {
    resolve('p3')
  })
}
Promise.all(['a','b',p2(),p3(),'c']).then(function (results) {
  // result => ['a','b','p2','p3','c']
})

代码设计: 因为是类点的一个方法,那么all应该属于Promise类的静态方法

  • 返回的是一个Promise对象,所以要创建一个promise对象
  • 然后遍历all数组里面的元素 为普通值直接存储结果,如果是promise对象则调用此对象获取相应结果,成功直接存储值,失败直接返回原因
  static all(array) {
    let result = []
    // 定义一个方法将 结果值放入 result中
    function addData(key,value) {
      result[key] = value
    }
    return new MyPromise((resolve, reject) => {
      for(let i = 0; i < array.length; i++) {
        let current = array[i];
        if(current instanceof MyPromise) {
          // Promise对象
          current.then(value => addData(i, value), reason => reject(reason));
        } else {
          // 普通值
          addData(i, current);
        }
      }
      return resolve(result);
    })
  }

  // demo
  function p2 () {
    return new MyPromise((resolve, reject) => {
      setTimeout(() => {resolve('p2')}, 2000)
    }).then(value => console.log(value))
  }
  function p3 () {
    return new MyPromise((resolve, reject) => {
      resolve('p3')
    })
  }
  MyPromise.all(['a','b',p2(),p3(),'c']).then(function (results) {
    // result => ['a','b','p2','p3','c']
    console.log(results);
  })
  // [ 'a', 'b', <1 empty item>, 'p3', 'c' ]

发现样例代码输出的不是我们想要的,异步的地方没有拿到相应的值。我们发现如果按照我们实现的all执行代码,for循环会同步立即执行完,这时候异步代码还没有执行,所以拿的值是空值。

解决这个问题我们可以声明一个索引值index,每次添加完一个结果值我们加1,知道index的值和数组长度相等 我们再调用resolve,即可.

  static all(array) {
    let result = []
    let index = 0
    return new MyPromise((resolve, reject) => {
      // 定义一个方法将 结果值放入 result中
      function addData(key,value) {
        result[key] = value
        index++;
        if(array.length === index) {
          resolve(result)
        }
      }
      for(let i = 0; i < array.length; i++) {
        let current = array[i];
        if(current instanceof MyPromise) {
          // Promise对象
          current.then(value => addData(i, value), reason => reject(reason));
        } else {
          // 普通值
          addData(i, current);
        }
      }
      return resolve(result);
    })
  }

2. Promise.resolve方法的实现

这个api就很简单:

  • 就是Promise类的方法
  • 接收一个参数 如果是promise对象则直接返回,如果是普通值则将这个值封装成一个promise对象并将参数值返回即可
  static resolve(value) {
    if(value instanceof MyPromise) return value;
    return new MyPromise((resolve, reject) => resolve(value))
  }

3. finally 方法的实现

需求分析

  • 无论这个Promise对象是成功还是失败,这个finally里面的回调都会执行一遍
  • finally后面可以链式调用then方法拿到promise相应状态的结果值
  • 如果finally的返回值是异步的promise,应该需要等待这个异步代码返回结果了,我们才执行callback,后面的链式then才开始执行

代码设计

  • 有一点我们可以确定就是,所有promise实例都应该有这个finally方法,所以这个方法应该在promise原型上
  • 通过then我们就可以拿到promise的状态值,解决上面第一个问题
finally(callback) {
  this.then(() => {
    callback()
  },() => {
    callback()
  })
}
finally(callback) {
    return this.then(value => {
      callback()
      return value
    },reason => {
      callback()
      throw reason
    })
  }
  • 因为then返回的就是promise对象,所以直接返回 then的值,就可以达到链式调用的效果,将then里面的成功回调值返回就可以向下传递promise成功的值,同理抛出失败的原因。
finally(callback) {
  return this.then(value => {
    callback()
    return value
  },reason => {
    callback()
    throw reason
  })
}
  • 第三个问题,我们应该借助promise的resolve方法将 finally的callback()返回的值封装一层为promise对象,当异步回来我们就再将promise对象返回的结果向下传递
finally(callback) {
  return this.then(value => {
    return MyPromise.resolve(callback()).then(() => value)
  },reason => {
    return MyPromise.resolve(callback()).then(() => {throw reason})
  })
}

catch的实现

catch方法是处理promise失败回调的,也就是说在我们使用then方法的时候我们是可以不传递失败回调的。如果我们不传递,则会被catch捕获

代码实现很简单,只要在catch里面执行then方法,将失败回调传递进去即可

catch(failCallback) {
    return this.then(undefined,failCallback)
  }

最后奉上 MyPromise 全部代码实现

/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected'// 失败

class MyPromise {
  constructor(executor) {
    // Promise创建立即执行执行器
    // 捕获并处理执行器里面的错误
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  // Promise 状态
  status = PENDING
  // value reason属于Promise实力
  // 成功之后的值
  value = void 0;
  // 失败后的原因
  reason = void 0;
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];
  // 为什么使用箭头函数,是为了resolve 和 reject里面的this指向的是 这个Promise对象
  // 为什么使用常量不直接写字符串 1. 编辑器会有提示,2. 可以复用

  resolve = value => {
    // 如果状态不是pending 阻止程序向下执行
    if(this.status !== PENDING ) return;
    // 将状态更改为成功
    this.status = FULFILLED
    // 保存成功后的值
    this.value = value
    // 遍历所有then里面的成功回调
    while(this.successCallback.length) this.successCallback.shift()()
  }

  reject = reason => {
    // 如果状态不是pending 阻止程序向下执行
    if(this.status !== PENDING ) return;
    // 将状态更改为失败
    this.status = REJECTED
    // 保存失败后的原因
    this.reason = reason
    // 遍历所有then里面的失败回调
    while(this.failCallback.length) this.failCallback.shift()()
  }
  // Promise的原型
  then(successCallback, failCallback) {
    // 判断是否有成功()回调,如果没有则给一个默认函数 value => value
    // 判断是否有失败回调,如果没有则给一个默认函数 reason => {throw reason}
    successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => {throw reason}
    let promise2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if(this.status === FULFILLED) {
        // 这里使用这个这段代码就会异步 在生成promise2对象之后再执行 真泥马巧妙
        setTimeout(() => {
          // 捕获then回调的错误并处理
          try {
            let x = successCallback(this.value);
            // 判断x的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promise对象的返回结果
            // 在根据promise对象返回的结果 决定调用reslove 还是reject
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        },0)
      } else if(this.status === REJECTED) {
        setTimeout(() => {
          // 捕获then回调的错误并处理
          try {
            let x = failCallback(this.reason);;
            // 判断x的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promise对象的返回结果
            // 在根据promise对象返回的结果 决定调用reslove 还是reject
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        },0)
      } else {
        // 状态为等待
        // 把每次then里面成功回调和失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            // 捕获then回调的错误并处理
            try {
              let x = successCallback(this.value);
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          },0)
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            // 捕获then回调的错误并处理
            try {
              let x = failCallback(this.reason);;
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          },0)
        });
      }
    });
    return promise2
  }
  finally(callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    },reason => {
      return MyPromise.resolve(callback()).then(() => {throw reason})
    })
  }
  catch(failCallback) {
    return this.then(undefined,failCallback)
  }
  static all(array) {
    let result = []
    let index = 0
    return new MyPromise((resolve, reject) => {
      // 定义一个方法将 结果值放入 result中
      function addData(key,value) {
        result[key] = value
        index++
        if(array.length === index) {
          resolve(result)
        }
      }
      for(let i = 0; i < array.length; i++) {
        let current = array[i];
        if(current instanceof MyPromise) {
          // Promise对象
          current.then(value => addData(i, value), reason => reject(reason));
        } else {
          // 普通值
          addData(i, current);
        }
      }
    })
  }
  static resolve(value) {
    if(value instanceof MyPromise) return value;
    return new MyPromise((resolve, reject) => resolve(value))
  }
}
function resolvePromise(promise2, x, resolve, reject) {
  if(promise2 === x) {
    return reject(new TypeError('Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>'))
  }
  if(x instanceof MyPromise) {
    // promise 对象
    // x.then(value => resolve(value), reason => reject(reason));
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}
module.exports = MyPromise