JS内功修炼(es6篇)之手撸简易版本promise

382 阅读3分钟

promiseA+规范

常见术语

1) promise: 一个有then方法的对象或者函数,它的行为遵从promiseA+规范
2) thenable: 一个有then方法的对象或者函数
3) value: promise执行成功时候的值,也是resolve的参数,可以是任意数据类型
4) reason: promise执行失败时候的值,也是reject的参数
5) exception: throw 抛出来的异常

promise status 状态流转

promise 有三种状态:可以实现状态之间的流转。
1) pending 状态 初始状态 可变
    可以通过resolve变成fulfilled状态
    可以通过reject变成rejected状态
2) fulfilled 执行成功时候的状态 最终状态 不可变
    通过resolve变成fulfilled状态
    必须有一个value值
3) reject 执行失败时候的状态
    通过reject变成rejected状态
    必须有一个reason值

then promise应该提供一个then方法,用来访问最后的结果

   promise.then(onFulfilled, onRejected)
1.onFulfilled和onRejected必须是函数,如果不是函数,应该被忽略
2.onFulfilled和onRejected必须是微任务 使用queueMicrotask
3.onFulfilled和onRejected都只能被调用一次,当promise的状态变成fulfilled的时候,应该调用
onFulfilled,当promise的状态变成rejected的时候,应该调用onRejected。
4.then方法可以被调用多次,当promise状态变成fulfilled后,所有的onFulfilled回调都需要按照
then的顺序执行,也就是按照注册的顺序执行。当promise状态变成rejected后,所有的onRejected回调
都需要按照then的顺序执行,也就是按照注册的顺序执行。
5.then 方法的返回值是一个新的promise
6.resolvePromise
  resolvePromise(promise2, x, resolve, reject)
6.1 如果x和prommise2相等 则抛出异常,避免死循环
6.2 如果x是一个promise 递归调用resolvePromise
6.3 如果x是一个object或者是一个function
let then = x.then
如果x.then出错,那么 reject 出去
then是一个函数 则 then.call(x, resolvePromiseFn, rejectPromise)
如果then不是一个函数 则直接resolve出去

基于promiseA+规范实现一个简易版本promise

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MPromise {
  FULFILLED_CALLBACK_LIST = []
  REJECTED_CALLBACK_LIST =[]
  _status = PENDING

  constructor (fn) {
    this.status = PENDING;
    this.value = null;
    this.reason = null;
    try {
      fn(this.resolve.bind(this), this.reject.bind(this));
    } catch (err) {
      this.reject(err);
    }
  }

  get status () {
    return this._status;
  }

  set status (newStatus) {
    this._status = newStatus;
    switch (this._status) {
      case FULFILLED:
        this.FULFILLED_CALLBACK_LIST.forEach(callback => {
          callback(this.value);
        });
        break;
      case REJECTED:
        this.REJECTED_CALLBACK_LIST.forEach(callback => {
          callback(this.reason);
        });
        break;
    }
  }

  resolve (value) {
    if (this.status !== PENDING) {
      return;
    }
    this.value = value;
    this.status = FULFILLED;
  }

  reject (reason) {
    if (this.status !== PENDING) {
      return;
    }
    this.reason = reason;
    this.status = REJECTED;
  }

  then (onFulfilled, onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value;
    const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => { throw reason; };

    const promise2 = new MPromise((resolve, reject) => {
      const fulfilledMicroTask = () => {
        queueMicrotask(() => {
          try {
            const x = realOnFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      };

      const rejectedMicroTask = () => {
        queueMicrotask(() => {
          try {
            const x = realOnRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      };

      switch (this.status) {
        case FULFILLED:
          fulfilledMicroTask();
          break;
        case REJECTED:
          rejectedMicroTask();
          break;
        case PENDING:
          this.FULFILLED_CALLBACK_LIST.push(fulfilledMicroTask);
          this.REJECTED_CALLBACK_LIST.push(rejectedMicroTask);
          break;
      }
    });
    return promise2;
  }

  // core 用来解析一个promise
  resolvePromise (promise2, x, resolve, reject) {
    // x 与 promise2相等直接抛出错误 避免死循环
    if (x === promise2) {
      return reject(TypeError('the x and promise are the same'));
    }
    // x是MPromise实例
    if (x instanceof MPromise) {
      x.then(y => {
        this.resolvePromise(promise2, y, resolve, reject);
      }, reject);
    }
    // 是函数或者是一个对象
    if (this.isFunction(x) || typeof x === 'object') {
      if (x === null) {
        return resolve(x);
      }
      let then = null;
      try {
        then = x.then;
      } catch (err) {
        return reject(err);
      }
      if (this.isFunction(then)) {
        let called = false;
        then.call(x,
          y => {
            if (called) {
              return;
            }
            called = true;
            this.resolvePromise(promise2, y, resolve, reject);
          },
          r => {
            if (called) {
              return;
            }
            called = true;
            reject(r);
          }
        );
      } else {
        resolve(x);
      }
    } else {
      resolve(x);
    }
  }

  // catch
  catch (onRejected) {
    return this.then(null, onRejected)
  }


  // 静态方法 可以直接调用

  static resolve (value) {
    if (value instanceof MPromise) {
      return value;
    }
    return new MPromise((resolve, reject) => {
      resolve(value);
    });
  }

  // reject
  static reject (reason) {
    return new MPromise((resolve, reject) => {
      reject(reason);
    });
  }

  // race: 竞速方法 有一个Promise执行成功之后 后续代码不再执行
  static race (promiseList = []) {
    return new MPromise((resolve, reject) => {
      const len = promiseList.length;
      if (!len) {
        return resolve();
      }
      for (let i = 0; i < len; i++) {
        MPromise.resolve(promiseList[i]).then(
          value => resolve(value),
          reason => reject(reason)
        );
      }
    });
  }

  // all: Promise.all可以将多个Promise实例包装成一个新的Promise实例。Promise.all的成功结果数组
  和传入的数组的顺序是一样的
  static all (promiseList = []) {
    return new MPromise((resolve, reject) => {
      const len = promiseList.length;
      let resolvedNum = 0;
      const resolvedValueList = new Array(len);
      for (let i = 0; i < len; i++) {
        MPromise.resolve(promiseList[i]).then(value => {
          resolvedNum++;
          resolvedValueList[i] = value;
          if (resolvedNum === len) {
            resolve(resolvedValueList);
          }
        }, reason => reject(reason));
      }
    });
  }

  // 工具函数 判断是否是function
  isFunction (value) {
    return typeof value === 'function';
  }
}