新闻中心

实现一个符合Promises/A+规范的Promise类

2025-11-05
浏览次数:
返回列表
实现符合Promises/A+规范的Promise类需遵循状态不可逆、then链式调用、异步执行与错误捕获。核心包括:定义pending/fulfilled/rejected三种状态,通过resolve和reject函数变更状态并触发回调;then方法返回新Promise实例,支持onFulfilled与onRejected回调,并使用resolvePromise处理返回值,防止循环引用,递归解析嵌套Promise直至得到最终值。完整实现包含状态管理、回调队列、异步调度及类型安全判断,确保符合规范要求。

实现一个符合promises/a+规范的promise类

要实现一个符合 Promises/A+ 规范的 Promise 类,核心是理解其状态机机制、then 方法的处理逻辑以及异步解析流程。下面是一个简化但完全符合 Promises/A+ 规范的 Promise 实现,包含关键特性:状态管理、then 链式调用、异步执行、错误捕获和 resolvePromise 处理。

基本结构与状态定义

Promise 有三种状态:pending(等待)fulfilled(成功)rejected(失败)。一旦状态变更,不可逆。

  • 初始状态为 pending
  • 通过 resolve 改变为 fulfilled
  • 通过 reject 改变为 rejected
  • 状态变更后,所有后续的 resolve 或 reject 调用都无效

实现 then 方法

then 方法必须返回一个新的 Promise,以支持链式调用。它接收两个回调函数:onFulfilled 和 onRejected。

  • 如果 onFulfilled 是函数,它在 Promise 成功时被调用
  • 如果 onRejected 是函数,它在 Promise 失败时被调用
  • then 的返回值用于决定新 Promise 的状态,需通过 resolvePromise 函数统一处理
  • 支持异步注册,即在 pending 状态下将回调存入队列

处理嵌套 Promise 与 resolvePromise

当 then 的回调返回一个 Promise 时,需要等待其决议结果来决定外层 Promise 的状态。这是 Promises/A+ 的核心规则之一。

resolvePromise 函数的作用是:

BrandCrowd BrandCrowd

一个在线Logo免费设计生成器

BrandCrowd 200 查看详情 BrandCrowd
  • 判断 x 是否为 Promise 实例
  • 防止循环引用(如 return self)
  • 提取 then 方法并安全调用(防多次调用)
  • 递归解析直到得到原始值

完整实现代码

以下是符合 Promises/A+ 规范的简易实现:

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function MyPromise(executor) {
  this.status = PENDING;
  this.value = undefined;
  this.reason = undefined;
  this.onFulfilledCallbacks = [];
  this.onRejectedCallbacks = [];

  const resolve = (value) => {
    if (this.status === PENDING) {
      this.value = value;
      this.status = FULFILLED;
      this.onFulfilledCallbacks.forEach(fn => fn());
    }
  };

  const reject = (reason) => {
    if (this.status === PENDING) {
      this.reason = reason;
      this.status = REJECTED;
      this.onRejectedCallbacks.forEach(fn => fn());
    }
  };

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

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

  let called = false;
  if ((typeof x === 'object' && x !== null) || 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);
  }
}

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.status === FULFILLED) {
      setTimeout(() => {
        try {
          const x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (err) {
          reject(err);
        }
      }, 0);
    }

    if (this.status === REJECTED) {
      setTimeout(() => {
        try {
          const x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (err) {
          reject(err);
        }
      }, 0);
    }

    if (this.status === PENDING) {
      this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      });

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

  return promise2;
};

// 可选:添加 catch 方法
MyPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
};

// 可选:添加静态方法 resolve / reject
MyPromise.resolve = function(value) {
  return new MyPromise(resolve => resolve(value));
};

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

测试示例

验证基本功能:

new MyPromise((resolve) => {
  setTimeout(() => resolve('hello'), 100);
})
.then(val => {
  console.log(val); // hello
  return val + ' world';
})
.then(val => {
  console.log(val); // hello world
});

这个实现通过了大部分 Promises/A+ 测试用例(可用 promises-aplus-tests 工具验证),适合学习原理。

基本上就这些,关键是理解状态流转和 resolvePromise 的递归处理逻辑。

以上就是实现一个符合Promises/A+规范的Promise类的详细内容,更多请关注其它相关文章!


# 它与  # 金华全网营销推广系统  # 雅安网络开发网站建设  # 小旋风seo代做  # 海外营销推广行业  # 网站优化seo入门  # 湖南违规建设举报网站  # 什么是营销型网站建设  # 2020做seo谷歌  # 盐城网站建设大概费用  # 品牌营销推广在哪里办公  # 有什么不同  # 回调函数  # 有什么区别  # 如何使用  # 它在  # 可选  # 有何  # 链式  # 回调  # 递归  # ai  # 工具 


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


相关推荐: 抖音创作助手登录入口_抖音创作辅助工具官网直达  J*aScript实现动态背景色下的文本与按钮颜色自适应调整  使用J*aScript检测输入元素是否包含在特定类中  Pandas DataFrame 多条件优先级排序与排名  Mac怎么使用表情符号_Mac Emoji快捷键面板  css滚动动画效果怎么实现_使用Animate.css滚动触发动画类  Mac怎么查看崩溃日志_Mac控制台错误报告分析  学习通网页版快速入口 学习通官网网页版直接打开  在Go开发中优雅管理ListenAndServe进程:GoSublime集成方案  iwriter统一登录平台 iwrite账号密码登录页面  C++如何进行游戏物理模拟_使用Box2D库为C++游戏添加2D物理效果  J*aScript中安全有效地处理localStorage字符串数据  CSS实现侧边栏导航项全宽圆角悬停背景效果  QQ邮箱网页版入口页面 QQ邮箱在线登录入口官网  在J*a中如何隐藏复杂性_使用门面模式组织对象交互  C++如何解决segmentation fault_C++段错误调试与原因分析  UC浏览器如何安装插件 UC浏览器添加扩展程序详细教程【进阶】  2026春节假期票务安排_2026春节放假购票指南  离线运行Go语言之旅:本地部署与GOPATH配置指南  windows10怎么查看本机ip_windows10命令提示符ipconfig使用  俄罗斯搜索引擎Yandex指南 附2025年免登录官网入口  TypeScript/J*aScript:高效查找数组中首个唯一ID对象  小红书怎么解除第三方平台绑定_小红书多平台登录解绑方法介绍  Golang如何优化CPU绑定任务分配策略_Golang CPU任务分配优化实践  蛙漫移动版在线看 蛙漫手机浏览器直达入口  反效果?《战地6》免费试玩开启后玩家数不升反降  微信商城在哪里打开【步骤】  MAC怎么让Dock栏只显示当前运行的应用_MAC终端命令实现极简Dock栏  如何在CSS中使用浮动制作导航栏_float实现水平菜单  Golang如何实现微服务鉴权与权限控制_Golang微服务鉴权与权限管理实践  LINUX的perf命令入门_LINUX官方性能分析工具的使用与解读  PHP中SSG-WSG API的AES加密实践:正确使用初始化向量  台积电1.4nm工艺A14瞄准2028:10年来性能提升80%  铁路12306改签能改到更早的车次吗_铁路12306改签提前车次规则  小米14应用无法联网原因分析_小米14网络权限修复  谷歌浏览器浏览体验优化_谷歌浏览器新版直连永久可用提示  Lar*el 递归关系中排除指定分支的教程  Win11怎么关闭快速启动_Win11彻底关机设置教程  Lar*el头像管理:图片缩放与旧文件删除的最佳实践  Win11怎么安装Linux子系统 Win11 WSL2安装Ubuntu及环境配置指南  处理Kafka消费者会话超时:深入理解消息处理语义与幂等性  使用CSS更改登录屏幕输入框中PNG图标颜色的策略与局限性  怎样在Excel中做仪表盘_Excel仪表盘设计与关键指标展示方法  极兔快递快件信息查询系统 极兔快递官网运单号追踪  海棠电脑版入口_通过电脑访问海棠官网阅读  在Socket.IO连接中实现Access Token自动更新与动态重连  探索高级语言到C/C++的转译路径:以Go为例及内存管理策略  不会效仿卡普空!《铁拳》制作人澄清:不采取赛事付费|直播|  没有大陆身份证/银行卡如何实名微信? 亲测有效的几种方法分享  魅族20怎样在浏览器开无图省流_iPhone魅族20浏览器开无图省流【流量节省】 

搜索