手写Promise(Promise A+规范)

149 阅读6分钟
// 记录Promise的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  /**
   * 创建一个Promise
   * @param {Function} executor 任务执行器,立即执行
   */
  constructor(executor) {
    this._state = PENDING; // 状态
    this._value = undefined; // 数据
    this._handlers = []; // 处理函数形成的队列

    // Promise执行过程中,一旦报错直接reject
    // 使用try...catch捕获错误
    try {
      // executor是调用Promise时传递的函数参数
      // 属于同步执行代码,所以直接调用
      executor(this._resolve.bind(this), this._reject.bind(this));
      // 使用bind的原因:resolve改变当前promise实例的数据和状态,
      // 使用了this(class在严格模式下执行,this会指向undefined)
      // 所以使用bind改变this指向,指向当前Promise实例
    } catch (error) {
      this._reject(error);
      console.error(error);
    }
  }

  /**
   * 向处理队列中添加一个函数
   * @param {Function} executor 添加的函数
   * @param {String} state 该函数什么状态下执行
   * @param {Function} resolve 让then函数返回的Promise成功
   * @param {Function} reject 让then函数返回的Promise失败
   */
  _pushHandler(executor, state, resolve, reject) {
    // 放入任务队列的executor可能为undefined、123
    // 为undefined的情况:then方法值传递了一个thenable事件处理函数,没有传递catchable
    // 只负责将任务放入任务队列,不负责处理以上的情况,以上情况将在executor执行的时候处理
    // 同时存入resolve和reject,根据相应的state执行resolve或reject
    this._handlers.push({
      executor,
      state,
      resolve,
      reject,
    });
  }

  /**
   * 根据实际情况,执行队列
   * 调用时机:
   * 1.状态改变的时候(resolve和reject) --> _changState
   * 2.const p = new Promsie((resolve, reject) => { resolve(1) });
   *   p.then((data) => { console.log(data) });
   *   调用then的时候,状态已经改变
   */
  _runHandlers() {
    if (this._state === PENDING) {
      // 目前任务仍在挂起
      return;
    }

    while (this._handlers[0]) {
      const handler = this._handlers[0];
      this._runOneHandler(handler);
      // 避免注册多个then方法后,先注册的处理函数在后注册then执行时再次执行
      this._handlers.shift(); 
    }
  }

  /**
   * 处理一个handler
   * @param {Object} handler
   */
  _runOneHandler({ executor, state, resolve, reject }) {
    runMicroTask(() => {
      if (this._state !== state) {
        // 状态不一致,不处理
        return;
      }

      if (typeof executor !== 'function') {
        // 传递后续处理并非一个函数
        // then(() => {})只传递了一个函数,或者then(123)

        // const pro1 = new Promise((resolve, reject) => {})
        // const pro2 = pro1.then(() => {});
        // 如果pro1失败了,pro2没有失败的处理
        // pro1的状态是什么,pro2的状态就是什么

        // 值穿透(状态穿透)
        // resolve、reject是then返回的Promise的,由前一个Promise的状态决定调用
        this._state === FULFILLED ? resolve(this._value) : reject(this._value);
        return;
      }

      try {
        const result = executor(this._value);
        if (isPromise(result)) {
          // thenable函数返回的是一个promise
          // 那么then返回的promise的状态和thenable返回的promise的状态保持一致
          result.then(resolve, reject);
        } else {
          resolve(result);
        }
      } catch (error) {
        reject(error);
        console.error(error);
      }
    });
  }

  /**
   * Promise A+规范的then
   * @param {Function} onFulfilled 成功之后调用的函数
   * @param {Function} onRejected 失败之后调用的函数
   * then函数(onFulfilled、onRejected)不会立即执行,也不会立即加入到微任务队列
   * then要做的事:将onFulfilled、onRejected放入任务队列
   */
  then(onFulfilled, onRejected) {
    // 没有立即把onFulfilled和onRejected放入任务队列的原因
    // 如果立即加入任务队列,多次调用then,任务队列中有多个函数[fn1, fn2, ...fnn]
    // 没有办法知道任务队列中的函数是成功之后执行还是失败之后执行

    // 调用_pushHandler给函数标记状态并放入任务队列

    // 返回一个新的Promise
    return new MyPromise((resolve, reject) => {
      // 同时传递resolve和reject
      this._pushHandler(onFulfilled, FULFILLED, resolve, reject);
      this._pushHandler(onRejected, REJECTED, resolve, reject);
      // 调用then的时候,可能状态已经改变,所以需要调用一次
      this._runHandlers(); // 执行队列
    });
  }

  /**
   * 仅处理失败的场景
   * @param {Function} onRejected
   */
  catch(onRejected) {
    return this.then(null, onRejected);
  }

  /**
   * 无论成功还是失败都会执行回调onSettled
   * @param {Function} onSettled
   */
  finally(onSettled) {
    // finally就是一个特殊的then
    
    // return this.then(onSettled, onSettled);
    // 会导致前一个promise的值传递到onSettled

    // data和reason是前一个promise的resolve的值或执行错误的原因
    // onSettled如果执行报错,传递给then的onFulfilled或onRejected就会报错,finally返回的promise就会是rejected
    return this.then(
      (data) => {
        onSettled();
        return data;
      },
      (reason) => {
        onSettled();
        throw reason;
      }
    );
  }

  /**
   * 更改任务状态
   * @param {String} newState 新状态
   * @param {any} value 相关数据
   */
  _changeState(newState, value) {
    if (this._state !== PENDING) {
      // 目前状态已经更改
      return;
    }
    this._state = newState;
    this._value = value;
    this._runHandlers(); // 状态变化,执行队列
  }

  /**
   * 标记当前任务完成
   * @param {any} data 任务完成的相关数据
   */
  _resolve(data) {
    // 改变状态_state和数据_value
    this._changeState(FULFILLED, data);
  }

  /**
   * 标记当前任务失败
   * @param {any} reason 任务失败的相关数据
   */
  _reject(reason) {
    // 改变状态_state和数据_value
    this._changeState(REJECTED, reason);
  }

  /**
   * 返回一个已完成的Promise
   * 特殊情况:
   * 1. 传递的data本身就是ES6的Promise对象
   * 2. 传递的data是PromiseLike(Promise A+),返回新的Promise,状态和其保持一致即可
   * @param {any} data
   */
  static resolve(data) {
    if (data instanceof MyPromise) {
      return data;
    }

    return new MyPromise((resolve, reject) => {
      if (isPromise(data)) {
        // data是PromiseLike
        data.then(resolve, reject);
      } else {
        resolve(data);
      }
    });
  }

  /**
   * 得到一个被拒绝的Promise
   * @param {any}} reason
   */
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }

  /**
   * 得到一个新的Promise
   * 该Promise的状态取决于proms的执行
   * proms是一个迭代器,包含多个Promise
   * 全部Promise成功,则返回的Promise成功,数据为所有Promise成功的数据,并且顺序是按照传入的顺序排列
   * 只要有一个Promise失败,则返回的Promise失败,原因是第一个失败的Promise的原因
   * @param {iterator} proms
   */
  static all(proms) {
    return new MyPromise((resolve, reject) => {
      try {
        const results = [];
        let count = 0; // Promise的总数
        let fulfilledCount = 0; // 已完成的数量
        for (const p of proms) { // 传递的proms可能是一个迭代器,迭代器不能使用for循环
          // 迭代器不一定有length属性
          // let i = count;
          // count++;
          let i = count++;

          // 不直接使用p.then(data => result.push(data))
          // 是因为没办法确定哪个promise先完成,就无法保准result是按顺序排列的
          
          // 使用MyPromise.resolve是因为proms中传递的有可能不是promise
          // 如果传递给resolve的是一个promise旧直接返回了
          MyPromise.resolve(p).then((data) => {
            fulfilledCount++;
            results[i] = data;
            if (fulfilledCount === count) {
              // 当前是最后一个Promise完成了
              resolve(results);
            }
          }, reject);
        }

        // 传递的proms数组是空数组,直接返回空数组
        if (count === 0) {
          resolve(results);
        }
      } catch (error) {
        reject(error);
        console.error(error);
      }
    });
  }

  /**
   * 等待所有的Promise有结果之后
   * 该方法返回的Promise完成
   * 并且按照顺序将所有结果汇总(不管成功还是失败)
   * @param {iterator} proms
   */
  static allSettled(proms) {
    const ps = [];
    for (const p of proms) {
      ps.push(
        MyPromise.resolve(p).then(
          (value) => ({
            status: FULFILLED,
            value,
          }),
          (reason) => ({
            status: REJECTED,
            reason,
          })
        )
      );
    }
    return MyPromise.all(ps);
  }

  /**
   * 返回的Promise与第一个有结果的一致
   * @param {iterator} proms
   */
  static race(proms) {
    return new MyPromise((resolve, reject) => {
      for (const p of proms) {
        MyPromise.resolve(p).then(resolve, reject);
      }
    });
  }
}

/**
 * 判断一个数据是否是Promise对象
 * @param {any} obj
 * @returns
 */
function isPromise(obj) {
  return !!(obj && typeof obj === 'object' && typeof obj.then === 'function');
}

/**
 * 运行一个微队列任务
 * 把传递的函数放到微队列中
 * @param {Function} callback
 */
function runMicroTask(callback) {
  // 判断node环境
  // 为了避免「变量未定义」的错误,这里最好加上前缀globalThis
  // globalThis是一个关键字,指代全局对象,浏览器环境为window,node环境为global
  if (globalThis.process && globalThis.process.nextTick) {
    process.nextTick(callback);
  } else if (globalThis.MutationObserver) {
    const p = document.createElement('p');
    const observer = new MutationObserver(callback);
    observer.observe(p, {
      childList: true, // 观察该元素内部的变化
    });
    p.innerHTML = '1';
  } else {
    setTimeout(callback, 0);
  }
}

function delay(duration) {
  return new Promise(resolve => {
    setTimeout(resolve, duration)
  });
};


const pro = new Promise((resolve, reject) => {
  resolve(1);
  // reject(1); // pro2也会是失败的
});

// 一般地,pro2的状态和pro完全一致,和finally的执行没有关系
// 但是finall执行中报错,pro2的状态会是rejected的
const pro2 = pro.finally((d) => {
  console.log('finally', d); // finally undefined

  // 返回结果是无效的,不会改不
  return 123;
});