ES6 Promise

108 阅读2分钟
class Promise {
  constructor(execute) {
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallback = [];
    this.onRejectedCallback = [];

    try {
      execute(this.resolvePromise.bind(this), this.rejectPromise.bind(this));
    } catch (error) {
      this.rejectPromise(error);
    }
  }

  /** resolve,Promise execute 第一参数
   *  pending => fulfilled 时需要判断 value是否是promise,及thenable对象,如果是,则状态需要与它保持一致,否则进行状态扭转
   * !!! 请注意,这里 resolveFulfilledValue 第三个参数为 resolve,而非resolePromise,因为进入 resolveFulfilledValue 函数执行完成后,
   * 不会在存在 thenable对象(即使存在thenthen的值也非函数,不需要考虑)不需要再次判断,否则如果then为属性时,会陷入无限循环判断
   *
   * !!! 为什么不在 resolvePromise 进行 then获取和判断,因为测试用例对 x.then 获取次数进行了限制,获取1次为 Function,大于1次为null(坑的很)
   * @param {*} value
   */
  resolvePromise(value) {
    if (this.status === 'pending') {
      if (value !== null && typeof value === 'object' || typeof value === 'function') {
        resolveFulfilledValue(this, value, this.resolve.bind(this), this.rejectPromise.bind(this));
      } else {
        // 如果不是promise,thenable对象,则resolve
        this.resolve(value);
      }
    }
  }

  // 从 resolvePromise 抽离出来,单独称为修改 fulfilled 状态方法
  resolve(value) {
    this.status = 'fulfilled';
    this.value = value;

    setTimeout(() => {
      this.onFulfilledCallback.forEach(fn => fn(value));
    }, 0);
  }

  /**
   * reject,Promise execute 第二参数
   * @param {*} reason
   */
  rejectPromise(reason) {
    if (this.status === 'pending') {
      this.status = 'rejected';
      this.reason = reason;

      setTimeout(() => {
        this.onRejectedCallback.forEach(fn => fn(reason));
      }, 0);
    }
  }


  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
      return value
    };
    onRejected = typeof onRejected === 'function' ? onRejected : function (reason) {
      throw reason
    };

    const _this = this,
      status = _this.status;
    let promise2;

    if (status === 'fulfilled') {
      promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          try {
            // 处理 onFulfilled 返回值, 判断返回值是否我promise,thenable对象
            resolveFulfilledValue(promise2, onFulfilled(_this.value), resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      });

    } else if (status === 'rejected') {
      promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          try {
            // 处理 onRejected 返回值
            resolveFulfilledValue(promise2, onRejected(_this.reason), resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      });
    } else {
      promise2 = new Promise((resolve, reject) => {
        _this.onFulfilledCallback.push(value => {
          try {
            resolveFulfilledValue(promise2, onFulfilled(value), resolve, reject);
          } catch (error) {
            reject(error);
          }
        });

        _this.onRejectedCallback.push(reason => {
          try {
            resolveFulfilledValue(promise2, onRejected(reason), resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      });
    }

    return promise2;
  }
}

function resolveFulfilledValue(promise, x, resolve, reject) {
  if (promise === x) {
    reject(TypeError());
    return;
  }

  const _type = typeof x;

  if ((_type === 'function' || _type === 'object') && x !== null) {
    let called = false; // 保证resolve,reject只执行一次
    try {
      // 务必保证 then 只获取一次
      const then = x.then;
      if (typeof then === 'function') {
        then.call(
          x,
          value => {
            if (called) return;
            called = true;
            resolveFulfilledValue(promise, value, resolve, reject);
          },
          reason => {
            if (called) return;
            called = true;
            reject(reason);
          }
        );
      } else {
        if (called) return;
        called = true;
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    resolve(x);
  }
}

module.exports = Promise;

// 测试用例
Promise.deferred = function () {
  let dfd = {};
  dfd.promise = new Promise(function (resolve, reject) {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd
};