手写Promise源码过Promise A+规范

适用人群

可熟练使用Promise

前言

关于手写Promise的源码之前尝试了不止4,5次但都以失败告终,不是觉得难不想坚持就是项目加急没有时间导致搁置。这次下定决心要花一个完整的时间段攻破这个事情,因为脑袋不是很灵光,总的算下来差不多4-5个小时,但是这里结果最重要,终于按照可以理解的思路完成了通过Promise A+规范的Promise代码编写!下面根据我的编写过程总结一下编码思路方便之后的理解。

准备工作

需要通过Promise A+规范,那就需要有测试用例来帮助我们判断自己写的的代码是否会经得住Promise A+的考验。所以我们可以准备一个专门用来检测Promise A+规范的npm包,来帮助我们判断编写的代码是否已经成功通过Promise A+规范。

添加promises-aplus-tests

npm包管理工具可以根据自己的习惯选取使用,这里使用yarn

复制yarn add promises-aplus-tests -D

修改package.json中的scripts

"scripts": {
    "test": "promises-aplus-tests MyPromise.js"
}

MyPromise.js换成自己写的代码文件,如果有文件夹嵌套需要带上路径。

这样我们就可以在认为代码编写完成后使用yarn test测试自己的代码了。

Promise A+规范

既然我们写的代码要通过Promise A+规范,我们就得要了解规范里的内容是什么。关于规范相关的内容,可以在这里看👉👉👉https://promisesaplus.com/。因为不能一屏截出来,所以就不贴出来了,感兴趣的同学可以进去看。

Promise基本特征

我们先梳理一番自己对Promise的了解,便于后续的理解。

  • promise函数对象的构造函数有一个回调函数作为参数,我们称为c0,回调函数会在函数执行时同步执行
  • c0的参数中又有两个回调函数,我们称为c1,c2
  • c1、c2会在c0中的我们指定的位置执行
  • promise的初始状态为pending,内部的初始值为undefined
  • c1执行后promise的状态会变为fulfilled,如果Promise有then方法,则then方法会被异步执行
  • c2执行后promise的状态会变为rejected,如果Promise有catch方法,则catch方法会被异步执行
  • c1、c2只会被执行一个
  • then函数中的返回值可以任意值,也可以是新的promise,不可以是本promise,会报错提示Chaining cycle detected for promise #<Promise>
  • then函数中返回新的promise后,下面接着的then、catch会跟着新的promise的状态走
  • 多个then函数在promise的状态转为fulfilled之后会依次异步执行

源码

executor的错误捕获没有处理

PENDING = "pending";
FULFILLED = "fulfilled";
REJECTED = "rejected";

class MyPromise {
  state = PENDING;
  result = void 0;
  _resolveArr = [];
  _rejectedArr = [];
  constructor(executor) {
    const resolve = (value) => {
      if (this.state === PENDING) {
        this.state = FULFILLED;
        this.result = value;
        this._resolveArr.forEach((func) => func());
      }
    };

    const reject = (reason) => {
      if (this.state === PENDING) {
        this.state = REJECTED;
        this.result = reason;
        this._rejectedArr.forEach((func) => func());
      }
    };

    executor(resolve, reject);
  }

  _handleSettledFunc(promise, x, resolve, reject) {
    // 检测循环调用
    if (x === promise) {
      reject(new TypeError("Chaining cycle detected for promise #<Promise>"));
    }

    // then中返回新的promise时使用新promise状态。x为假值时忽略.then的判断
    if (x && (typeof x === "object" || typeof x === "function")) {
      let done = false;
      // 捕获x.then及then.call的报错
      try {
        const then = x.then;
        if (typeof then === "function") {
          then.call(
            x,
            (y) => {
              if (done) {
                return;
              }
              done = true;
              this._handleSettledFunc(promise, y, resolve, reject);
            },
            (r) => {
              if (done) {
                return;
              }
              done = true;
              reject(r);
            }
          );
        } else { // 没有报错直接resolve
          resolve(x);
        }
      } catch (e) {
        if (done) {
          return;
        }
        done = true;
        reject(e);
      }
    } else {
      resolve(x);
    }
  }

  /**
   * 返回一个promise对象
   * @param {function} onFulfilled resolve后需要执行的函数
   * @param {function} onRejected reject后需要执行的函数
   * @returns
   */
  then(onFulfilled, onRejected) {
    const promise = new MyPromise((resolve, reject) => {
      onFulfilled =
        typeof onFulfilled === "function" ? onFulfilled : (value) => value;
      onRejected =
        typeof onRejected === "function"
          ? onRejected
          : (reason) => {
              throw reason;
            };

      const _handleFunc = (handleFunc) => {
        // 只能在这使用setTimeout,否则promise变量不能传递,报未初始化的错
        setTimeout(() => {
          // 捕获handleFunc(this.result)中的报错
          try {
            this._handleSettledFunc(
              promise,
              handleFunc(this.result),
              resolve,
              reject
            );
          } catch (error) {
            reject(error);
          }
        });
      };
      // 返回的函数会被异步调用
      if (this.state === FULFILLED) {
        _handleFunc(onFulfilled);
      } else if (this.state === REJECTED) {
        _handleFunc(onRejected);
      } else {
        // 数组形式保存onFulfilled、onRejected函数
        this._resolveArr.push(() => _handleFunc(onFulfilled));
        this._rejectedArr.push(() => _handleFunc(onRejected));
      }
    });
    return promise;
  }
}

MyPromise.defer = MyPromise.deferred = function () {
  let dfd = {};
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};
module.exports = MyPromise;

参考链接

https://promisesaplus.com/

promises-aplus-tests 测试手写promise过程 - 365/24/60 - 博客园

https://juejin.cn/post/6844903796129136654

posted @   酉云良  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示