PromiseA+规范

promisesaplus.com/

简单的 promise 是什么?(thenable/PromiseLike

  • 对象
  • 有一个 then 方法

但是一个真正的 promise 除去这两点还有一些细化的东西,具体是一个怎么样的对象,then 方法内部是什么处理

javascript 复制代码
function isPromiseLike(value) {
  return (
    value !== null &&
    (typeof value === "object" || typeof value === "function") &&
    typeof value.then === "function"
  );
}

实现 PromiseA+ 规范

  1. 定义工具方法,用于 class 中的内部使用

    • 定义一个检测是否函数的方法
    js 复制代码
    function isFunction(x) {
      return typeof x === "function";
    }
    • 定义一个判断是否 thenablePromiseLike)的方法
    scss 复制代码
    function isThenable(x) {
      // 判断 x 是不是一个对象或者函数,并且 x.then 是一个函数
      return (
        ((typeof x === "object" && x !== null) || isFunction(x)) &&
        isFunction(x.then)
      );
    }
  2. 初始化一个 promise 构造函数,包含属性的定义,一个 constructor 构造方法以及 then 方法声明 定义内部变量,包含 statepromise 的状态),data(完成状态下的数据),reason(拒绝状态下的数据),settledhandlers(完成状态下的回调函数)

    js 复制代码
    class MyPromise {
      // Promise有三种状态
      _state = "pending"; // 初始状态'pending' | 'fulfilled' | 'rejected'
      _data = undefined; // promise 完成状态下的相关数据
      _reason = undefined; // promise 拒绝状态下的相关数据
      _settledhandlers = []; // 当前 promise 完成状态下的回调函数,为什么要定义成一个数组,是因为可能会有多个 then 方法
    ​
      constructor(executor) {}
    ​
      then(onFulfilled, onRejected) {}
    }

    为什么这些属性没有设置私有

    Promise/A+ 规范只要求状态不可变没要求可见性 ; ES6 官方 Promise 把 [[PromiseState]] 做成引擎级私有槽,外部无法访问; 自己实现时,用 #stateWeakMap 把它设为私有,是最佳实践而非强制。

  1. 完善构造函数的实现

    js 复制代码
    // 构造函数接受一个执行器函数 executor
      constructor(executor) {
        // executor 必须是一个函数
        if (!isFunction(executor)) {
          throw new TypeError(`Promise resolver ${executor} is not a function`);
        }
        // 定义实例属性 resolve 和 reject
        const resolve = (data) => {
          resolvePromise(this, data);
        };
        // 这个简单
        const reject = (err) => {
          rejectPromise(this, err);
        };
        try {
          // 执行过程中有错误,会直接抛出错误
          executor(resolve, reject);
        } catch (e) {
          return reject(e);
        }
      }

    具体的 resolvereject 需要定义额外的方法去实现

    • 其中,reject 方法考虑的内容较少,实现较为简单(只关注两件事,改变状态和更新 reason

      js 复制代码
      function rejectPromise(prom, reason) {
        // 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退
        if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected
        // 直接拒绝,修改promise的状态
        prom._state = "rejected";
        prom._reason = reason;
      }
    • resolve 方法的实现,判断传入的值是不是一个 promise,要去吸收状态,否则直接改变 promise 的状态

      js 复制代码
      function resolvePromise(prom, x) {
        // 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退
        if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected
        //   判断 x 是不是一个 promise(有可能是一个 thenable 对象)
        if (isThenable(x)) {
          // 判断 x 和 prom 是不是同一个对象
          if (x === prom) {
            return rejectPromise(
              prom,
              new TypeError("Chaining cycle detected for promise #<Promise>")
            );
          }
          // 不是同一个对象,要去吸收 x 的状态,还要增加一个 microtask 队列
          queueMicrotask(() => {
            x.then(
              (data) => {
                // x 成功了,用 data 去 resolve prom
                resolvePromise(prom, data); // 递归解析
              },
              (err) => {
                // x 失败了,用 err 去 reject prom
                rejectPromise(prom, err);
              }
            );
          });
        } else {
          prom._state = "fulfilled";
          prom._data = x;
        }
      }
  2. 实现 then 方法,就处理两个问题,回调什么时候调用,以及 then 方法的返回新的 promise 状态是什么

    js 复制代码
    then(onFulfilled, onRejected) {
        // then 方法返回一个新的 promise
        const prom = new MyPromise((resolve, reject) => {});
        // 为什么要定义成一个数组,是因为可能会有多个 then 方法
        // 这里的回调函数要等到当前 promise 状态确定后才能调用
        this._settledhandlers.push({
          onFulfilled,
          onRejected,
          prom, // 当前 then 方法返回的 promise
        });
        // 数组里面push的内容什么时候执行呢?当状态确定后执行  通过一个flushHandles函数
        flushHandlers(this);
        return prom;
      }

    flushHandlers 方法是依次执行 promise 完成状态下的回调函数,通过 then 方法添加

    js 复制代码
    // 将curpromise的_settledhandlers数组中的回调函数依次执行  所有回调依次处理
    function flushHandlers(curPromise) {
      if (curPromise._state === "pending") return; // 状态还是pending 直接返回
      // 状态已经确定了 可能是fulfilled 也可能是rejected
      // 让这些回调函数依次执行
      const settledhandlers = curPromise._settledhandlers; //取出所有的回调
      // 将后续的处理放入微任务队列中执行  是将整个放入,不是循环一个一个放入
      queueMicrotask(() => {
        while (settledhandlers.length) {
          // 这个prom 和传入的curPromise不一样,是then方法中new的那个promise
          // 取出第一个回调,需要清空原数组
          const { onFulfilled, onRejected, prom } = settledhandlers.shift();
          // 具体处理回调和prom的状态
          // 判断,要是 onfocusfilled/onRejected 不是函数,就直接用当前promise的值去resolve/reject
          if (!isFunction(onFulfilled) && curPromise._state === "fulfilled") {
            // 状态穿透
            resolvePromise(prom, curPromise._data);
            continue; // 继续下一个
          }
          // 同样的,当前状态为失败,但是不是函数
          if (!isFunction(onRejected) && curPromise._state === "rejected") {
            // 状态穿透
            rejectPromise(prom, curPromise._reason);
            continue; // 继续下一个
          }
          // 走到这里,说明是函数,那就运行其中一个回调,状态结果取决于回调的结果是否报错,因此要try catch
          let result;
          try {
            // 不报错就resolve;
            result =
              curPromise._state === "fulfilled"
                ? onFulfilled(curPromise._data)
                : onRejected(curPromise._reason);
          } catch (error) {
            // 报错就reject
            rejectPromise(prom, error);
            continue; // 继续下一个
          }
          // 走到这里,说明没有报错,用result去resolve
          // 只要返回了一个结果,不管是成功还是失败,都要用这个结果去resolve
          resolvePromise(prom, result);
        }
      });
    }

    该方法为处理后续的回调,在状态确定后 或者调用 then 方法时执行

    因此需要额外在状态确定时调用一次,涉及到状态调用的地方有 rejectPromiseresolvePromise 两个方法

    js 复制代码
    function rejectPromise(prom, reason) {
      // 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退
      if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected
      // 直接拒绝,修改promise的状态
      prom._state = "rejected";
      prom._reason = reason;
      // 状态确定后,去执行回调函数
      flushHandlers(prom);
    }
    function resolvePromise(prom, x) {
      // 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退
      if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected
      //   判断 x 是不是一个 promise(有可能是一个 thenable 对象)
      if (isThenable(x)) {
        xxxx.....
      } else {
        prom._state = "fulfilled";
        prom._data = x;
        // 状态确定后,去执行回调函数
        flushHandlers(prom);
      }
    }
  3. 调用测试,和 Promise 方法进行对比

    js 复制代码
    process.on("unhandledRejection", () => {
      console.log("process unhandledRejection");
    });
    const p = new Promise((resolve, reject) => {
      resolve(1);
    });
    const p_my = new MyPromise((resolve, reject) => {
      resolve(1);
    });
    console.log("start", p._state);
    const p1 = p.then(123).then(() => {
      return "p1 fulfilled";
    });
    const p2 = p_my.then(123).then(() => {
      return "p2 fulfilled";
    });
    setTimeout(() => {
      console.log("p=>", p);
      console.log("p_my=>", p_my);
      console.log("p1=>", p1);
      console.log("p2=>", p2);
    }, 0);
    ​
    // p=> Promise { 1 }
    // p_my=> MyPromise {
    //   _state: 'fulfilled',
    //   _data: 1,
    //   _reason: undefined,
    //   _settledhandlers: []
    // }
    // p1=> Promise { 'p1 fulfilled' }
    // p2=> MyPromise {
    //   _state: 'fulfilled',
    //   _data: 'p2 fulfilled',
    //   _reason: undefined,
    //   _settledhandlers: []
    // }

完整写法

js 复制代码
// 贴合es6的写法
function isFunction(x) {
  return typeof x === "function";
}
function isThenable(x) {
  // 判断 x 是不是一个对象或者函数,并且 x.then 是一个函数
  return (
    ((typeof x === "object" && x !== null) || isFunction(x)) &&
    isFunction(x.then)
  );
}
​
// 处理后续的回调  在状态确定后 或者 调用 then 方法执行
// 将curpromise的_settledhandlers数组中的回调函数依次执行  所有回调依次处理
function flushHandlers(curPromise) {
  if (curPromise._state === "pending") return; // 状态还是pending 直接返回
  // 状态已经确定了 可能是fulfilled 也可能是rejected
  // 让这些回调函数依次执行
  const settledhandlers = curPromise._settledhandlers; //取出所有的回调
  // 将后续的处理放入微任务队列中执行  是将整个放入,不是循环一个一个放入
  queueMicrotask(() => {
    while (settledhandlers.length) {
      // 这个prom 和传入的curPromise不一样,是then方法中new的那个promise
      // 取出第一个回调,需要清空原数组
      const { onFulfilled, onRejected, prom } = settledhandlers.shift();
      // 具体处理回调和prom的状态
      // 判断,要是 onfocusfilled/onRejected 不是函数,就直接用当前promise的值去resolve/reject
      if (!isFunction(onFulfilled) && curPromise._state === "fulfilled") {
        // 状态穿透
        resolvePromise(prom, curPromise._data);
        continue; // 继续下一个
      }
      // 同样的,当前状态为失败,但是不是函数
      if (!isFunction(onRejected) && curPromise._state === "rejected") {
        // 状态穿透
        rejectPromise(prom, curPromise._reason);
        continue; // 继续下一个
      }
      // 走到这里,说明是函数,那就运行其中一个回调,状态结果取决于回调的结果是否报错,因此要try catch
      let result;
      try {
        // 不报错就resolve;
        result =
          curPromise._state === "fulfilled"
            ? onFulfilled(curPromise._data)
            : onRejected(curPromise._reason);
      } catch (error) {
        // 报错就reject
        rejectPromise(prom, error);
        continue; // 继续下一个
      }
      // 走到这里,说明没有报错,用result去resolve
      // 只要返回了一个结果,不管是成功还是失败,都要用这个结果去resolve
      resolvePromise(prom, result);
    }
  });
}
​
function rejectPromise(prom, reason) {
  // 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退
  if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected
  // 直接拒绝,修改promise的状态
  prom._state = "rejected";
  prom._reason = reason;
  // 状态确定后,去执行回调函数
  flushHandlers(prom);
}
function resolvePromise(prom, x) {
  // 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退
  if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected
  //   判断 x 是不是一个 promise(有可能是一个 thenable 对象)
  if (isThenable(x)) {
    // 判断 x 和 prom 是不是同一个对象
    if (x === prom) {
      return rejectPromise(
        prom,
        new TypeError("Chaining cycle detected for promise #<Promise>")
      );
    }
    // 不是同一个对象,要去吸收 x 的状态,还要增加一个 microtask 队列
    queueMicrotask(() => {
      x.then(
        (data) => {
          // x 成功了,用 data 去 resolve prom
          resolvePromise(prom, data); // 递归解析
        },
        (err) => {
          // x 失败了,用 err 去 reject prom
          rejectPromise(prom, err);
        }
      );
    });
  } else {
    prom._state = "fulfilled";
    prom._data = x;
    // 状态确定后,去执行回调函数
    flushHandlers(prom);
  }
}
​
class MyPromise {
  // Promise有三种状态
  _state = "pending"; // 初始状态'pending' | 'fulfilled' | 'rejected'
  _data = undefined; // promise 完成状态下的相关数据
  _reason = undefined; // promise 拒绝状态下的相关数据
  _settledhandlers = []; // 当前 promise 完成状态下的回调函数
​
  // 构造函数接受一个执行器函数 executor
  constructor(executor) {
    // executor 必须是一个函数
    if (!isFunction(executor)) {
      throw new TypeError(`Promise resolver ${executor} is not a function`);
    }
    // 定义实例属性
    const resolve = (data) => {
      resolvePromise(this, data);
    };
    // 这个简单
    const reject = (err) => {
      rejectPromise(this, err);
    };
    try {
      // 执行过程中有错误,会直接抛出错误
      executor(resolve, reject);
    } catch (e) {
      return reject(e);
    }
  }
​
  // 就处理两个问题,回调什么时候调用,以及 then 方法的返回新的 promise 状态是什么
  then(onFulfilled, onRejected) {
    // 这里的回调函数要等到当前 promise 状态确定后才能调用
    const prom = new MyPromise((resolve, reject) => {});
    // 为什么要定义成一个数组,是因为可能会有多个 then 方法
    // 这里的回调函数要等到当前 promise 状态确定后才能调用
    this._settledhandlers.push({
      onFulfilled,
      onRejected,
      prom, // 当前 then 方法返回的 promise
    });
    // 数组里面push的内容什么时候执行呢?当状态确定后执行  通过一个flushHandles函数
    flushHandlers(this);
    return prom;
  }
}
相关推荐
江拥羡橙7 天前
JavaScript异步编程:告别回调地狱,拥抱Promise async/await
开发语言·javascript·ecmascript·promise·async/await
光影少年7 天前
Promise.all实现其中有一个接口失败其他结果正常返回,如何实现?
前端·promise·掘金·金石计划
李重楼15 天前
一次性帮你搞定 Promise 的四个静态方法
前端·promise
前端人类学18 天前
掌控异步洪流:多请求并发下的顺序控制艺术
javascript·promise
光影少年1 个月前
Promise状态和方法都有哪些,以及实现原理
javascript·promise·掘金·金石计划
CHANG_THE_WORLD1 个月前
C++并发编程指南 std::promise 介绍与使用
java·开发语言·c++·promise
一枚前端小能手1 个月前
🔄 Promise又写成回调地狱了?这是咋弄哩
前端·javascript·promise
乔公子搬砖1 个月前
小程序开发提效:npm支持、Vant Weapp组件库与API Promise化(八)
前端·javascript·微信小程序·js·promise·vagrant·事件绑定
flyliu1 个月前
真的要搞懂Promise的流程啊混蛋
前端·promise