新闻中心

手写一个符合Promises/A+规范的Promise_j*ascript技巧

2025-10-31
浏览次数:
返回列表
实现一个符合 Promises/A+ 规范的 Promise 需遵循状态不可变、异步执行、链式调用等规则,核心包括三种状态(pending、fulfilled、rejected)、then 方法返回新 Promise、resolvePromise 处理返回值、catch 和 finally 的语法糖实现,以及静态 resolve 和 reject 方法,完整覆盖规范关键机制。

手写一个符合promises/a+规范的promise_javascript技巧

实现一个符合 Promises/A+ 规范的 Promise 并不只是写个简单的异步容器,而是要严格遵循状态机制、异步解析和链式调用等核心规则。下面是一个精简但完全符合 Promises/A+ 规范核心逻辑的手写 Promise 实现,包含关键特性:状态管理、then 链式调用、值穿透、错误捕获以及异步执行。

Promise 的三种状态

Promise 有三个状态:

  • pending:初始状态,未完成也未拒绝
  • fulfilled:操作成功完成
  • rejected:操作失败

状态一旦从 pending 变为 fulfilled 或 rejected,就不能再改变。

基本结构与状态控制

function MyPromise(executor) {
  this.state = 'pending';
  this.value = undefined;
  this.reason = undefined;
  this.onFulfilledCallbacks = [];
  this.onRejectedCallbacks = [];

  const resolve = (value) => {
    if (this.state === 'pending') {
      this.state = 'fulfilled';
      this.value = value;
      this.onFulfilledCallbacks.forEach(fn => fn());
    }
  };

  const reject = (reason) => {
    if (this.state === 'pending') {
      this.state = 'rejected';
      this.reason = reason;
      this.onRejectedCallbacks.forEach(fn => fn());
    }
  };

  try {
    executor(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

构造函数接收一个执行器函数(executor),立即执行,并传入 resolve 和 reject 函数。通过闭包维护状态和回调队列。

实现 then 方法

then 方法是 Promise 的核心,必须返回一个新的 Promise,以支持链式调用。

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  // 处理透传值,默认函数
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
  onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };

  const promise2 = new MyPromise((resolve, reject) => {
    if (this.state === 'fulfilled') {
      setTimeout(() => {
        try {
          const x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    }

    if (this.state === 'rejected') {
      setTimeout(() => {
        try {
          const x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }, 0);
    }

    if (this.state === 'pending') {
      this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      });

      this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      });
    }
  });

  return promise2;
};

注意使用 setTimeout 模拟异步微任务(实际规范中应使用 queueMicrotask,但 setTimeout 在浏览器中可模拟异步行为)。

万相营造 万相营造

阿里妈妈推出的AI电商营销工具

万相营造 168 查看详情 万相营造

处理返回值 resolvePromise

这个函数决定如何处理 then 回调的返回值 x,是 Promises/A+ 最复杂的一部分。

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected'));
  }

  let called = false;

  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      const then = x.then;

      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, r => {
          if (called) return;
          called = true;
          reject(r);
        });
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}

该函数判断 x 是否为 Promise 或类 Promise 对象(有 then 方法),递归解析直到得到最终值。

补充方法:catch 和 finally

catch 是 then 的语法糖,只处理错误。

MyPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
};

MyPromise.prototype.finally = function(callback) {
  return this.then(
    value => MyPromise.resolve(callback()).then(() => value),
    reason => MyPromise.resolve(callback()).then(() => { throw reason; })
  );
};

finally 不影响结果值,无论成功或失败都会执行 callback。

静态方法:resolve 和 reject

MyPromise.resolve = function(value) {
  return new MyPromise(resolve => resolve(value));
};

MyPromise.reject = function(reason) {
  return new MyPromise((_, reject) => reject(reason));
};

基本上就这些。这个手写 Promise 覆盖了 Promises/A+ 的主要规范点:状态不可逆、then 返回新 Promise、异步执行、错误捕获、链式解析。虽然没有覆盖所有边界测试,但已足够理解其核心机制。在真实项目中推荐使用原生 Promise,但在面试或学习时,这种实现能深刻掌握异步编程原理。

以上就是手写一个符合Promises/A+规范的Promise_j*ascript技巧的详细内容,更多请关注php中文网其它相关文章!


# java  # 荣成可信网站建设  # 网站建设企业采购  # 搜狗流量seo案例  # 网店营销和推广推广方案  # 邢台专业网站建设工作  # 但在  # 就不  # 是一个  # 不匹配  # 中不  # 回调  # 三种  # 返回值  # 递归  # 链式  # ai  # 浏览器  # javascript  # 编程  # php  # 贵州seo优化公司平台  # 网站优化南通  # 东莞仓储seo软件有哪些  # seo要懂建站那些  # 营销推广策略定义 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: AWS EC2实例间SQL Server连接超时:安全组配置与故障排除指南  纯CSS与HTML网格布局的HTML精简策略:SVG与JS方案解析  UC浏览器如何安装插件 UC浏览器添加扩展程序详细教程【进阶】  PHP中获取MongoDB服务器运行时间(Uptime)的专业指南  Win11怎么设置开机NumLock亮 Win11修改注册表InitialKeyboardIndicators值  CSS实现侧边栏导航项全宽圆角悬停背景效果  邮政快递包裹最新位置 邮政快递实时追踪入口  126邮箱账号注册 电脑版登录入口  Discord Slash 命令响应超时问题的异步解决方案  零跑汽车11月交付量达70327台 实现连续9个月正增长  必由学登录入口 必由学官方网站在线访问链接  Yandex搜索引擎官网入口_俄罗斯Yandex免登录一键直达  顺丰快递查询系统 官方正版查询入口  谷歌浏览器无痕模式怎么开 Chrome开启无痕浏览设置方法【教程】  今日头条怎么同步内容到抖音_今日头条内容同步到抖音教程  vivo云服务网页版登录 怎么登录vivo云服务网页版  优化LangChain文档加载与ChromaDB集成:解决多文档处理与分块问题  极兔快递快件信息查询系统 极兔快递官网运单号追踪  没有大陆身份证/银行卡如何实名微信? 亲测有效的几种方法分享  CSS布局中意外空白:解决padding-top导致的顶部间距问题  中兴BladeV30怎样用测距估书架层高_iPhone中兴BladeV30测距估书架层高【家装参考】  多闪网页版在线观看免费入口_多闪官网访问入口  Python字典中优雅地迭代剩余元素的方法  CKEditor 5 自定义构建在React应用中渲染失败的调试与解决  2026年发布! 美少女养成动作RPG《神剑少女战记》发布实机演示  俄罗斯搜索引擎Yandex指南 附2025年免登录官网入口  Golang如何优化CPU绑定任务分配策略_Golang CPU任务分配优化实践  QQ邮箱登录平台入口 QQ邮箱网页版邮箱官方入口  Lar*el头像管理:图片缩放与旧文件删除的最佳实践  拼多多赚钱渠道_拼多多收益来源  Python getattr() 异常处理深度解析:避免程序意外退出  邮政编码查询不到怎么办_邮政编码查询不到的常见原因与对策  顺丰快递查单号物流信息 顺丰快递小程序查询入口  利用Bokeh CustomJS动态控制DataTable列可见性  QQ邮箱稳定登录入口_QQ邮箱官方网站网页版使用  双系统安装时,如何设置默认启动系统? msconfig命令了解一下!  格力空气能E5故障代码是什么情况_格力空气能E5代码解析与应对措施  优化Django表单:提交验证失败后保留用户输入  qq游戏大厅官方下载_qq游戏免费下载安装入口  Composer中的^和~符号代表什么_精通Composer版本号语义化约束  TikTok国际版官网直达_TikTok国际版官网直达进入在线观看  快速CSGO开箱网站指南 CSGO开箱平台推荐  为什么简单的XML文件也会解析失败? 检查隐藏的非打印字符(如BOM)的方法  TikTok网页版直接登录 TikTok网页端官方平台入口  Node.js中HTML按钮与J*aScript函数交互的正确姿势  最新韩小圈网页版登录入口_官网在线观看官方链接  C++如何检测键盘输入_C++ _kbhit与_getch函数非阻塞输入  生成rdflib自定义SPARQL函数:参数匹配与实践指南  夸克AO3官网入口_AO3镜像网站2025推荐  Golang如何安装Swagger工具_GoSwagger文档生成环境 

搜索