实现一个符合promiseA+规范的promise

315 阅读5分钟

一、什么是promise?

  • promise是一个有then方法的对象或者是函数,行为遵循本规范;
  • thenable是一个有then方法的对象或者是函数;
  • value是promise状态成功时的值,也就是resolve的参数,包括各种数据类型,也包括undefined/thenable或者是promise;
  • reason是promise状态失败时的值,也就是reject的参数,表示拒绝的原因;
  • exception是一个使用throw抛出的异常值。

二、A+规范

1、promise的三种状态
  • pending

    1.1 初始的状态,可改变;

    1.2 一个promise在resolve或者reject前都处于这个状态;

    1.3 可以通过resolve->fulfilled状态;

    1.4 可以通过reject->rejected状态;

  • fulfilled

    1.1 最终态,不可变;

    1.2 一个promise被resolve后会变成这个状态;

    1.3 必须拥有一个value值

  • rejected

1.1 最终态,不可变;

1.2 一个promise被reject后是这个状态;

1.3 必须拥有一个reason

2、promise的状态流转
  • pending->resolve(value)->fulfilled
  • pending->reject(reason)->rejected

三、then

promise有一个then方法,用来访问最终的结果,无论是value还是reason。

promise.then(onFunfilled,onRejected)
1、两个参数:

1.1 onFulfilled必须是函数类型,如果不是函数,应该被忽略;

  • 在promise变成fulfilled时,应该调用onFulfilled,参数是value;
  • 在promise变成fulfilled之前,不应该被调用;
  • 只能被调用一次(所以在实现的时候需要一个变量来限制执行次数) 1.2 onRejected必须是函数类型,如果不是函数,应该被忽略;
  • 在promise变成rejected时,应该调用onRejected,参数是reason;
  • 在promise变成rejected之前,不应该被调用;
  • 只能被调用一次(所以在实现的时候需要一个变量来限制执行次数)
2、onFulfilled和onRejected是微任务

使用queueMicrotask来实现微任务的调用。

3、then方法可以被调用多次

3.1 promise状态变成fulfilled后,所有的onFulfilled回调都需要按照then的顺序执行,也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onFulfilled的回调);

3.2 promise的状态变成rejected后,所有的onRejected回调都需要按照then的顺序执行,也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onRejected的回调)

4、then方法的返回值

4.1 then返回的是一个全新的promise

promise2 - promise1.then(onFulfilled, onRejected);

4.2 设onFulfilled或者onRejected执行的结果为x,调用resolvePromise(后面解释);

4.3 如果onFulfilled或者onRejected执行时抛出异常e,promise2需要被reject;

4.4 如果onFulfilled不是一个函数,promise2以promise1的value触发fulfilled;

4.5 如果onRejected不是一个函数,promise2以promise1的reason触发rejected;

5、重点解释下resolvePromise
// 用来分类处理返回值
resolvePromise(promise2, x, resolve, reject)

5.1 如果promise2和x相等,那么reject TypeError;

5.2 如果x是一个promise,则使newPromise接受x的状态;继续执行x,如果执行的时候拿到一个y,还要继续解析y;

5.3 如果x是一个object或者是一个function,

let then = x.then; //try catch包一下
如果是一个函数,
then.call(x, resolvePromiseFn, rejectPromise);
resolvePromiseFn的入参是y,执行resolvePromise(promise2, y,resolve, reject);
如果不是一个函数,则直接resolve(x)

四、代码实现

// status的三种状态

const PENDING = 'pending';

const FULFILLED = 'fulfilled';

const REJECTED = 'rejected';

\


class MPromise {

  // 一个细节考察点

  // 用数组来存回调函数,不适用于链式调用.then中,因为每一个.then返回的都是一个新的promise,

  // 当调用的promise实例是同一个的时候,数组就有意义了

  FULFILLED_CALLBACK_LIST = [];

  REJECTED_CALLBACK_LIST = [];

  // status的状态需要监听值的变化,实用getter\setter

  _status = PENDING;

  constructor(fn) {

    // 初始状态为pending

    this.status = PENDING;

    // 成功的初始值

    this.value = null;

    // 失败的初始值

    this.reason = null;

    try {

      fn(this.resolve.bind(this), this.reject.bind(this));

    } catch (e) {

      this.reject(e);

    }

  }

\


  // 获取status的值

  get status() {

    return this._status;

  }

  // status值发生变化

  set status(newStatus) {

    this._status = newStatus;

    switch (newStatus) {

      case FULFILLED: {

        this.FULFILLED_CALLBACK_LIST.forEach(callback => {

          callback(this.value);

        });

        break;

      }

      case REFUSED: {

        this.REJECTED_CALLBACK_LIST.forEach(callback => {

          callback(this.reason)

        });

        break;

      }

    }

  }

\


  resolve(value) {

    // 只有状态为pending的前提下,值和状态才能变成成功或是失败

    if (this.status === PENDING) {

      this.value = value;

      this.status = FULFILLED;

    }

  }

\


  reject(reason) {

    if (this.status === PENDING) {

      this.reason = reason;

      this.status = REJECTED;

    }

  }

\


  // then方法

  then(onFulfilled, onRejected) {

    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {

      return value

    }

    const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {

      throw reason

    }

    // then方法返回的是一个新的promise2

    // onFulfilled和onRejected是微任务,使用queueMicrotask来实现微任务的调用

    const promise2 = new MPromise((resolve, reject) => {

      // 成功

      const fulfilledMicrotask = () => {

        queueMicrotask(() => {

          try {

            const x = realOnFulfilled(this.value);

            // 调用此方法来分类处理x得到的值

            this.resolvePromise(promise2, x, resolve, reject)

          } catch (e) {

            reject(e);

          }

        })

      };

      // 失败

      const rejectedMocrotask = () => {

        queueMicrotask(() => {

          try {

            const x = realOnFulfilled(this.reason);

            // 分类处理

            this.resolvePromise(promise2, x, resolve, reject)

          } catch (e) {

            reject(e)

          }

        })

      }

\


      // 根据状态来调用方法

      switch (this.status) {

        case FULFILLED: {

          fulfilledMicrotask()

          break;

        }

        case REJECTED: {

          rejectedMocrotask()

          break;

        }

        case PENDING: {

          this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask);

          this.REJECTED_CALLBACK_LIST.push(rejectedMocrotask);

        }

      }

    })

    return promise2;

  }

\


  catch(onRejected) {

    return this.then(null, onRejected)

  }

\


  // 判断参数是否是函数

  isFunction(param) {

    return typeof parame === 'function';

  }

\


  // 分类处理then的返回值

  resolvePromise(promise2, x, resolve, reject) {

    // 如果newPromise和x指向同一个对象,以typeerror拒绝执行newPromise

    // 这是为了防止死循环

    if (promise2 === x) {

      return reject(new TypeError('the promise and the return value are the same'))

    }

\


    // 如果x是promise

    if (x instanceof MPromise) {

      // 如果x为promise,则继续执行x,如果执行的结果是y,需要继续判断y

      queueMicrotask(() => {

        x.then((y) => {

          this.resolvePromise(promise2, y, resolve, reject)

        }, reject)

      })

    } else if (typeof x === 'object' || this.isFunction(x)) {

      // 如果x为对象或者函数

      // null也会被判断为对象

      if (x === null) {

        return resolve(x);

      }

\


      let then = null;

      try {

        then = x.then;

      } catch (error) {

        return reject(error);

      }

\


      // 如果then是函数

      if (this.isFunction(then)) {

        let called = false;

        // 将x作为函数的作用域this调用

        // 传递两个回调函数作为参数,第一个参数是resolvePromise,第二个参数叫做rejectPromise

        try {

          then.call(

            x,

            // 如果resolvePromise以值y为参数被调用,则运行resolvePromise

            (y) => {

              // 需要以一个变量called来保证只调用一次

              if (called) return;

              called = true;

              this.resolvePromise(promise2, y, resolve, reject)

            },

            // 如果rejectPromise以据因r为参数被调用,则以据因r拒绝promise

            (r) => {

              if (called) return;

              called = true;

              reject(r);

            }

          )

        } catch (e) {

          if (called) return;

          reject(e)

        }

      } else {

        // 如果then不是函数

        resolve(x)

      }

    } else {

      // 如果x不为对象或者函数,以x为参数执行promise

      resolve(x)

    }

  }

\


  // 静态方法resolve

  static resolve(value) {

    if (value instanceof MPromise) {

      return value;

    }

\


    return new MPromise((resolve) => {

      resolve(value);

    })

  }

\


  // 静态方法 reject

  static reject(reason) {

    return new MPromise((resolve, reject) => {

      reject(reason);

    })

  }

\


  // 静态方法 race,竞赛,一个先完成就结束

  static race(promiseList) {

    return new MPromise((resolve, reject) => {

      const length = promiseList.length;

      if (length === 0) {

        return resolve();

      } else {

        for (let i = 0; i < length; i++) {

          MPromise.resolve(promiseList).then(

            (value) => {

              return resolve(value);

            },

            (reason) => {

              return reject(reason);

            }

          )

        }

      }

    })

  }

\


  // 静态方法 all ,等到所有的promise都执行完

  static all(promiseList) {

    let index = 0;

    let result = [];

    return new MPromise((resolve, reject) => {

      promiseList.forEach((p, i) => {

        // 使用MPromise.resolve(p)用于处理传入值不为promise的情况

        MPromise.resolve(p).then(

          value => {

            index++;

            result[i] = value;

            if (index === promiseList.length) {

              resolve(result)

            }

          },

          reason => {

            reject(reason)

          }

        )

      })

    })

  }

}

\


// 写一个简单的测试用例

const test = new MPromise((resolve, reject) => {

  setTimeout(() => {

    reject(111);

  }, 1000);

}).then((value) => {

  console.log('then');

}).catch((reason) => {

  console.log('catch');

})

\