如何实现一个符合Promise A+规范的完整Promise库?

实现一个符合 Promise A+ 规范的 Promise 库,需定义 PENDING、FULFILLED、REJECTED 三种状态,构造函数执行 executor 并传入 resolve 和 reject 方法,通过 onFulfilledCallbacks 和 onRejectedCallbacks 存储异步回调;then 方法返回新 Promise,根据当前状态异步执行 onFulfilled 或 onRejected,并调用 resolvePromise 解析返回值 x;resolvePromise 函数处理 x 为 promise 或 thenable 的情况,防止循环引用并确保状态只改变一次;最后补充 MyPromise.resolve、reject、all、race 等静态方法以完善功能。整个实现需严格遵循规范对状态机、异步执行、错误捕获和链式调用的要求。

如何实现一个符合promise a+规范的完整promise库?

实现一个符合 Promise A+ 规范的 Promise 库,核心是理解并正确处理状态机、异步解析和 then 方法的链式调用。重点在于遵循规范中对 thenable 处理、状态不可逆变更、异步执行以及错误捕获的要求。

1. 定义基本状态与结构

Promise 有三种状态:pending、fulfilled、rejected,状态只能从 pending 变为 fulfilled 或 rejected,且一旦确定不可更改。

使用常量表示状态:PENDING = ‘pending’, FULFILLED = ‘fulfilled’, REJECTED = ‘rejected’ 每个 Promise 实例保存当前状态(this.state)、成功值(this.value)、失败原因(this.reason) 维护 onFulfilled 和 onRejected 回调队列,用于处理异步 resolve/reject 的情况

构造函数接收一个 executor 函数,立即执行,并传入 resolve 和 reject 方法:

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 (err) {    reject(err);  }}

2. 实现 then 方法

then 是 Promise A+ 的核心,必须返回一个新的 Promise,以支持链式调用。它接收两个可选参数:onFulfilled 和 onRejected。

根据当前状态决定如何处理回调:同步 resolved 就直接执行,异步则暂存到队列 如果 onFulfilled/onRejected 是函数,必须异步执行(使用 setTimeout 模拟微任务) 返回新 Promise,其状态由回调的返回值或异常决定

关键逻辑在处理返回值 x,需调用 resolvePromise 函数进行“解析”:

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;};

3. 实现 resolvePromise 辅助函数

这个函数处理 then 中回调返回的值 x,判断是否为 Promise 或 thenable 对象,决定如何 resolve 新的 promise2。

如果 x 和 promise2 相同,抛出 TypeError(防止循环引用) 如果 x 是对象或函数,尝试读取其 then 属性(注意可能抛错) 如果 x 有 then 方法且是函数,则认为是 thenable,将其作为 Promise 处理 否则将 x 作为普通值 resolve

function resolvePromise(promise2, x, resolve, reject) {  if (x === promise2) {    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);  }}

4. 补充常用方法

虽然 Promise A+ 只规定了 then,但完整的库通常包含以下静态方法:

MyPromise.resolve(value):返回一个 resolved 状态的 Promise MyPromise.reject(reason):返回一个 rejected 状态的 Promise MyPromise.all(promises):全部完成才 resolve,任意失败则 reject MyPromise.race(promises):首个完成的 Promise 决定结果

例如 all 的实现:

MyPromise.all = function(promises) {  return new MyPromise((resolve, reject) => {    const results = [];    let completedCount = 0;    const total = promises.length;    if (total === 0) {      resolve(results);      return;    }    for (let i = 0; i  {        results[i] = value;        completedCount++;        if (completedCount === total) {          resolve(results);        }      }, reject);    }  });};

基本上就这些。只要严格按照 Promise A+ 规范处理状态流转、回调调度和链式解析,就能实现一个合规且可用的 Promise 库。测试时建议使用官方 promises-aplus-tests 工具验证兼容性。

以上就是如何实现一个符合Promise A+规范的完整Promise库?的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1525671.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月20日 17:52:18
下一篇 2025年12月8日 11:44:24

相关推荐

  • 使用 async/await 实现 JavaScript 中的异步加载效果

    本文将介绍如何使用 async/await 关键字在 JavaScript 中实现异步加载效果,避免 setTimeout 和 setInterval 带来的回调地狱问题。通过创建一个简单的 sleep 函数,结合 async/await,可以使代码看起来更像同步执行,从而提高代码的可读性和可维护性…

    好文分享 2025年12月20日
    000
  • JavaScript正则表达式:利用负向先行断言捕获未匹配的剩余内容

    本文深入探讨在JavaScript中使用正则表达式时,如何有效捕获那些未被前置特定分组匹配的字符串片段。通过引入负向先行断言(Negative Lookahead)技术,我们展示了一个通用的模式,该模式能确保正则表达式始终匹配整个字符串,并能将未匹配的部分归入一个单独的捕获组,从而解决传统方法如.*…

    2025年12月20日
    000
  • 使用 Eel 和 Python 实现前端图片实时更新的教程

    在使用 Eel 框架构建 Web 应用时,有时会遇到这样的问题:Python 后端执行耗时操作时,前端页面会卡住,直到后端操作完成才能响应。这会导致前端图片无法及时更新,影响用户体验。本文将介绍一种解决方案:使用 Celery 异步任务队列,将耗时操作放入后台执行,从而确保前端能够及时响应并更新图片…

    2025年12月20日
    000
  • JavaScript中的代码审查(Code Review)有哪些要点?

    代码审查需关注功能正确性、变量函数设计、编码规范及性能安全。1. 确保逻辑完整,异步处理和错误兜底到位;2. 命名清晰,作用域合理,函数单一职责;3. 遵循ESLint等风格规范,注释适度;4. 避免重复计算、内存泄漏,防范XSS,审慎使用第三方库。 代码审查在JavaScript开发中是保障代码质…

    2025年12月20日
    000
  • JavaScript中的代码混淆和压缩原理是什么?

    代码压缩通过去除空白、注释和简化表达式减小体积,混淆则通过重命名、控制流扁平化和字符串加密增加逆向难度,二者常结合使用以平衡性能与安全。 JavaScript代码混淆和压缩的目的是在不改变程序功能的前提下,减小文件体积并增加代码的阅读难度,主要用于提升加载性能和防止反向工程。虽然两者常被一起使用,但…

    2025年12月20日
    000
  • 使用 Vega 实现节点悬停高亮链接效果

    本文档将指导你如何在 Vega 可视化库中实现节点悬停时高亮显示相关链接的功能。通过监听鼠标事件,动态更新节点的颜色和链接的样式,从而增强交互性和信息展示效果。本文将提供详细的代码示例和步骤说明,帮助你快速掌握该技巧。 实现步骤 要在 Vega 中实现节点悬停高亮链接的效果,主要需要以下几个步骤: …

    2025年12月20日
    000
  • 如何实现一个轻量级的前端依赖注入(DI)容器?

    一个轻量级前端DI容器通过注册-解析机制实现依赖解耦,支持构造函数自动注入与单例管理,适用于插件系统、测试mock等场景,核心代码不足百行,提升可维护性与测试性。 前端依赖注入(DI)容器的核心目标是解耦组件与依赖的创建过程,提升可测试性和可维护性。实现一个轻量级的 DI 容器并不需要复杂的设计模式…

    2025年12月20日
    000
  • 在异步编程中,如何优雅地处理 Promise 的拒绝状态以避免未捕获的错误?

    要避免未捕获的Promise错误,需始终显式处理失败路径。1. 使用.catch()捕获链式错误,确保每个Promise链以.catch()结尾;2. 在async/await中用try/catch包裹await表达式;3. 监听unhandledrejection事件作为最后防线;4. 确保每个独…

    2025年12月20日
    000
  • 使用 Vega 实现节点悬停高亮特定链接

    本文档介绍了如何使用 Vega 可视化语法,在力导向图中实现节点悬停时高亮显示相关链接的功能。我们将通过修改 Vega 的配置,添加交互信号和条件样式,使得当鼠标悬停在节点上时,与其相连的链接能够突出显示,从而增强数据的可探索性和可视化效果。 实现节点悬停高亮链接 以下步骤将指导你如何在现有的 Ve…

    2025年12月20日
    000
  • 在JavaScript中,如何实现基于角色的访问控制(RBAC)?

    答案:JavaScript中RBAC通过角色判断权限,前端用rolePermissions对象定义角色权限,用户含roles数组,hasPermission函数遍历角色检查权限,用于控制UI展示如按钮显隐,但敏感操作须由后端验证,前端仅优化体验。 在JavaScript中实现基于角色的访问控制(RB…

    2025年12月20日
    000
  • Next.js环境下Top-Level-Await的正确配置指南

    当在Next.js项目中遇到top-level-await错误时,通常是因为Webpack的配置未正确启用该实验性功能。由于Next.js内部集成了Webpack,用户不应创建独立的webpack.config.js文件,而应通过修改next.config.js文件来配置Webpack。正确的做法是…

    2025年12月20日
    000
  • 在MERN应用中根据用户角色筛选文章:获取所有讲师发布的帖子

    本文详细介绍了在MERN堆%ignore_a_1%应用中,如何高效地根据用户角色(例如“讲师”)来筛选并获取其发布的所有文章。通过分步查询,首先识别出特定角色的用户,然后利用这些用户的ID来精确检索相关文章,从而解决直接在文章模型中按角色查询的难题。 理解问题:按关联模型属性筛选 在构建mern(m…

    2025年12月20日
    000
  • 如何在HTML页面中显示txt文件内容

    本文介绍了如何使用Flask框架将txt文件的内容传递到HTML页面并进行显示。通过Python读取txt文件,并利用Flask的render_template函数将读取到的内容作为变量传递给HTML模板,最后在HTML中使用Jinja2模板引擎的语法将内容渲染到页面上。 要在HTML页面中显示tx…

    2025年12月20日
    000
  • Next.js 动态路由参数 id 的获取与使用指南

    本文详细阐述了在 Next.js 应用中,如何正确地通过 params 对象获取动态路由 [id] 中的 id 参数,并在组件内部进行数据请求和渲染。文章强调了正确的文件结构、组件参数解构以及 useEffect 依赖项的设置,以确保动态页面能够准确地接收并利用路由参数,从而构建功能完善的动态内容展…

    2025年12月20日 好文分享
    000
  • V8 脚本编译缓存:字节码还是机器码?深入解析与应用

    本文旨在深入探讨 V8 引擎的脚本编译缓存机制,重点分析缓存数据中存储的是字节码还是机器码。通过对 V8 源码的解读,揭示了当前缓存主要包含字节码,并讨论了未来可能包含机器码的可能性。此外,文章还强调了缓存数据的平台依赖性和版本依赖性,为开发者在使用 V8 缓存机制时提供指导,避免潜在的兼容性问题。…

    2025年12月20日
    000
  • 如何实现JavaScript代码的懒加载与按需加载策略?

    使用动态import()和Intersection Observer实现按需加载,结合路由级代码分割与预加载提示,通过webpack或Vite构建工具优化资源加载时机,减少初始体积、提升首屏性能。 实现JavaScript代码的懒加载与按需加载,核心在于减少初始加载体积、提升页面响应速度。关键策略是…

    2025年12月20日
    000
  • 如何用CSS-in-JS方案实现动态主题切换?

    使用 styled-components 实现动态主题切换:1. 安装 styled-components 并定义浅色、深色主题对象;2. 用 ThemeProvider 包裹组件并传递当前主题;3. 在 styled 组件中通过 props.theme 引用主题值;4. 创建按钮触发状态更新以切换…

    2025年12月20日
    000
  • V8 缓存数据揭秘:字节码与机器码之争

    正如摘要所述,V8 引擎在缓存 JavaScript 代码时,主要存储的是字节码,而非直接生成并存储特定于目标平台的机器码。虽然未来可能会包含基线机器码,但目前缓存数据的构成主要围绕字节码展开。 V8 缓存数据格式 V8 的缓存数据格式是自定义的,并未公开。这意味着我们无法依赖标准化的方式来解析或操…

    2025年12月20日
    000
  • JavaScript 的代码混淆与压缩技术如何平衡安全性与可调试性?

    混淆与压缩需权衡安全与维护:压缩减小体积,混淆防逆向;应分环境处理,开发保留源码结构,生产适度混淆核心逻辑,配合source map与监控定位问题。 JavaScript 的混淆与压缩在提升性能和保护代码之间需要权衡,过度处理会影响调试效率,而处理不足则可能暴露逻辑。关键在于根据使用场景选择合适策略…

    2025年12月20日
    000
  • 如何设计一个支持热重载的JavaScript开发环境?

    答案是设计支持热重载的JavaScript开发环境需结合HMR机制与开发服务器,利用Webpack、Vite等工具的内置功能实现文件变更自动更新;通过WebSocket通知浏览器替换模块,配合框架专用插件如react-refresh或Vue Loader优化组件热更新,正确配置服务器路径与监听规则,…

    2025年12月20日
    000

发表回复

登录后才能评论
关注微信