Bluebird Promise.props 使用指南:并行等待对象属性与 Map 键值对中的 Promise
Bluebird Promise.props 使用指南并行等待对象属性与 Map 键值对中的 Promise【免费下载链接】bluebird:bird: :zap: Bluebird is a full featured promise library with unmatched performance.项目地址: https://gitcode.com/gh_mirrors/bl/bluebirdPromise.props是 Bluebird 提供的一个集合工具它把 Promise.all 的并行等待能力从数组扩展到普通对象和 ES6 Map传入一个键值对容器当其中所有值可以是 Promise、thenable 或普通值都兑现后返回一个携带相同键、值为最终结果的全新对象或 Map。本文围绕 docs/docs/api/props.md 与 docs/docs/api/promise.props.md 两份 API 文档展开并结合 src/props.js 源码与 test/mocha/props.js 测试讲解其行为规则、典型用法、与Promise.join的取舍以及底层实现原理。读完你将掌握如何用一条 API 优雅地并发等待一组关联数据如多个接口响应、文件统计结果并理解它为何对 Map 和对象采用不同的内部处理路径。API 签名与核心语义Promise.props(Object|Map|PromiseObject|Map input) - Promise输入一个普通对象、一个 ES6Map或者一个解析为上述两者的 Promise。输出一个 Promise当输入中所有属性/条目的值都兑现后该 Promise 兑现为一个全新对象或 Map各键对应各自的最终值。失败语义只要其中任意一个属性值 reject返回的 Promise 就以该拒绝原因为原因 reject失败优先不做等待聚合。它与.all的关系是.all针对数组按索引并行等待Promise.props针对对象属性或 Map 条目按键并行等待其余语义保持一致。官方文档原文也将其描述为 Like.allbut for object properties orMaps entries instead of iterated values详见 promise.props.md。行为规则与边界条件文档与源码共同确定了以下几类输入的处理规则理解这些规则是正确使用的前提受信任的 Promise 输入被拆包如果传入的是一个 Bluebird Promisetrusted Promise它会被当作一个最终会变成对象的 Promise来等待而不是按对象的属性处理——即先等它兑现出对象再对该对象执行props逻辑。普通对象按自身可枚举属性处理除 Map 之外的所有对象其属性集合等同于Object.keys(obj)返回的自身可枚举属性。这意味着原型链上的属性、不可枚举属性不会参与。数组也被当作对象处理Promise.props([1,2,3])会兑现为{0: 1, 1: 2, 2: 3}因为数组也是对象其索引正是自身可枚举属性。Map 的键不会被等待即使 Map 的键恰好是 Promise 实例它们也不会被 await结果 Map 仍然保留这些 Promise 实例作为键只等待值。这是文档明确标注的脚注行为**注。仅支持原生 ES6 Map文档脚注*注明确说明只支持环境自带的原生 ECMAScript 6Map实现。源码中通过if (typeof Map function) Es6Map Map;进行能力探测polyfill 或自定义类 Map 结构不在支持范围内。非对象输入直接拒绝传入undefined、字符串等原始值时返回的 Promise 以TypeError拒绝。错误消息定义在 src/constants.jscannot await properties of a non-object。基础示例并行加载多组数据文档给出的第一个示例直观展示了最常见的应用场景——并行发起多个请求并按键取回结果Promise.props({ pictures: getPictures(), comments: getComments(), tweets: getTweets() }).then(function(result) { console.log(result.tweets, result.pictures, result.comments); });getPictures()、getComments()、getTweets()三个异步调用同时启动互不阻塞当三者全部完成时result是一个与输入同构的对象可通过result.tweets等按键访问。相比手写三个.then嵌套或Promise.all后再手动组装键值映射Promise.props让关联数据的并行聚合一步到位。实战示例递归统计目录信息文档中的第二个示例是更完整的实战代码递归读取目录下所有文件同时统计目录数、文件数、总大小、最小文件与最大文件最后用Promise.props把多个独立的统计结果聚合到一个对象中一次性返回。该示例同时演示了promisifyAll、map、call等 Bluebird 常用 API 的配合使用var Promise require(bluebird); var fs Promise.promisifyAll(require(fs)); var _ require(lodash); var path require(path); var util require(util); function directorySizeInfo(root) { var counts {dirs: 0, files: 0}; var stats (function reader(root) { return fs.readdirAsync(root).map(function(fileName) { var filePath path.join(root, fileName); return fs.statAsync(filePath).then(function(stat) { stat.filePath filePath; if (stat.isDirectory()) { counts.dirs; return reader(filePath) } counts.files; return stat; }); }).then(_.flatten); })(root).then(_.chain); var smallest stats.call(min, size).call(pick, size, filePath).call(value); var largest stats.call(max, size).call(pick, size, filePath).call(value); var totalSize stats.call(pluck, size).call(reduce, function(a, b) { return a b; }, 0); return Promise.props({ counts: counts, smallest: smallest, largest: largest, totalSize: totalSize }); } directorySizeInfo(process.argv[2] || .).then(function(sizeInfo) { console.log(util.format( \n\ %d directories, %d files \n\ Total size: %d bytes \n\ Smallest file: %s with %d bytes \n\ Largest file: %s with %d bytes \n\ , sizeInfo.counts.dirs, sizeInfo.counts.files, sizeInfo.totalSize, sizeInfo.smallest.filePath, sizeInfo.smallest.size, sizeInfo.largest.filePath, sizeInfo.largest.size)); });值得注意的细节counts是一个普通对象其值在读取过程中被同步累加属于已经是最终值的属性——Promise.props对普通值直接保留不会报错smallest、largest、totalSize是从同一份stats派生出的三个独立 Promise由Promise.props并行等待只要counts、smallest、largest、totalSize全部就绪sizeInfo就同时携带四类信息随后用util.format一次性打印。与 Promise.join 的取舍文档特别指出如果对结果对象本身没有其他用途、只是想把几个值取出来用那么使用Promise.join更便捷——它直接把各结果作为回调参数传入省去按键取值的步骤Promise.join(getPictures(), getComments(), getTweets(), function(pictures, comments, tweets) { console.log(pictures, comments, tweets); });两者行为等价但接口形态不同场景推荐 API理由需要按键访问、结果要作为对象整体传递或继续参与后续props聚合Promise.props保留键值映射结构值数量固定、调用方只想拿到位置对应的若干结果Promise.join回调参数直接展开代码更短见 src/join.js实例方法 .props()除了静态方法文档还提供实例方法形态props.md.props() - Promise其语义与Promise.props(this)完全相同——调用者把自己当作属性容器传入。典型用法是配合链式调用前一步.then返回一个包含多个 Promise 属性的对象再通过.props()并行展开。实例方法的实现见 src/props.js只是一层转发Promise.prototype.props function () { return props(this); };源码级实现剖析src/props.js 是props功能的完整实现整个模块以工厂函数形式注入Promise、PromiseArray、tryConvertToPromise、apiRejection等内部依赖。核心逻辑分为三层1. 入口函数类型分派function props(promises) { var ret; var castValue tryConvertToPromise(promises); if (!isObject(castValue)) { return apiRejection(PROPS_TYPE_ERROR); } else if (castValue instanceof Promise) { ret castValue._then( Promise.props, undefined, undefined, undefined, undefined); } else { ret new PropertiesPromiseArray(castValue).promise(); } if (castValue instanceof Promise) { ret._propagateFrom(castValue, PROPAGATE_BIND); } return ret; }先用tryConvertToPromise尝试把输入转换为 Promise普通对象会保持原样返回非对象输入undefined、原始值走apiRejection(PROPS_TYPE_ERROR)快速失败对应 src/constants.js 中的错误常量输入是 Promise 时通过_then(Promise.props, ...)把等待对象先兑现的逻辑交给 Promise 自身调度兑现后再递归调用Promise.props——这正是文档中promise for object行为先拆包再按对象处理的实现普通对象/Map 则交给PropertiesPromiseArray迭代。2. PropertiesPromiseArray对象与 Map 的统一抽象PropertiesPromiseArray继承自基类 src/promise_array.js 的PromiseArray通过扁平化 entries把键值对适配成 PromiseArray 熟悉的线性索引模型Map 输入用mapToEntries把 Map 扁平化为[v0, v1, ..., k0, k1, ...]的双倍长度数组值在前半段、键在后半段并标记isMap true对象输入用es5.keys(obj)取自身可枚举键同样铺成[value..., key...]的扁平数组构造函数最后以RESOLVE_MAP/RESOLVE_OBJECT作为空容器标记传给_init见 src/props.js。PromiseArray._iteratesrc/promise_array.js对扁平数组逐项执行tryConvertToPromise已是 Promise 的条目走_proxy或直接按状态回调普通值则立即通过_promiseFulfilled记账。3. 结果重建全部就绪后按原结构组装子类覆写了三个关键方法src/props.js_promiseFulfilled(value, index)每有一个条目兑现就写入_values[index]并递增_totalResolved当totalResolved _length时根据_isMap分支重建结果——Map 用entriesToMap从扁平数组拼回原生 Map对象则用keyOffset this.length()定位后半段的键、逐个回填到{}中然后_resolve(val)shouldCopyValues返回false复用扁平数组本身避免额外拷贝Map 场景键值同时驻留数组无需复制getActualLength(len)返回len 1因为扁平数组长度是条目数的两倍实际条目数取其一半。这种值在前、键在后的布局使得同一个PromiseArray迭代引擎既能服务all/map等数组场景也能零成本复用给props。测试用例佐证test/mocha/props.js 完整覆盖了上述全部语义可作为行为契约使用类型校验should reject undefined、should reject primitive——非对象输入拒绝TypeError结果新对象should resolve to new object断言结果v ! o返回全新对象而非修改输入值类型普通值属性should resolve value properties、已兑现 Promise 属性should resolve immediate properties、延迟兑现属性should resolve eventual properties用setTimeoutdefer模拟异步失败优先should reject if any input promise rejects验证任一条目 reject 即整体 rejectPromise 输入should accept a promise for an object/should reject a promise for a primitive验证先拆包再处理thenableshould accept thenables in properties与should accept a thenable for thenables in properties验证 thenable 兼容数组treats arrays for their properties验证数组按索引属性处理Map仅在环境支持Map时运行常规 Map 值等待、doesnt await promise keys in es6 maps键为 Promise 时保持原键、empty map should resolve to empty map空 Map 兑现为空 Map。其中空 Map 兑现为空 Map依赖PromiseArray._init中的空值短路逻辑src/promise_array.js条目数为 0 时直接以RESOLVE_MAP对应的new Map()或RESOLVE_OBJECT对应的{}作为空结果兑现。使用注意事项小结输入必须是自身可枚举属性的对象或原生 ES6 Map其他类型字符串、数字、undefined、非原生 Map 结构会被拒绝结果永远是新对象/新 Map不会修改输入Map 的键即使包含 Promise 也不会被等待最终键保持为原 Promise 实例失败是先到先拒任一属性 reject整体立即 reject不会等待其余条目若只是固定数量值的展开使用优先考虑Promise.join见 docs/docs/api/promise.join.md需要按键访问或整体传递时再使用Promise.props。相关文档与源码入口docs/docs/api/props.md、docs/docs/api/promise.props.md、src/props.js、src/promise_array.js、src/constants.js、test/mocha/props.js。【免费下载链接】bluebird:bird: :zap: Bluebird is a full featured promise library with unmatched performance.项目地址: https://gitcode.com/gh_mirrors/bl/bluebird创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

GeoLibre 云原生 GIS 完整指南:5 分钟出图、不下载查询远程数据、嵌入网页

GeoLibre 云原生 GIS 完整指南:5 分钟出图、不下载查询远程数据、嵌入网页

GeoLibre 云原生 GIS 完整指南:5 分钟出图、不下载查询远程数据、嵌入网页 【免费下载链接】GeoLibre A lightweight, cloud-native GIS platform for visualizing, exploring, and analyzing geospatial data. It runs in the web browser, on the desktop, on mob…

2026/9/20 19:06:09 阅读更多 →
Android开发板ADB设备序列号重复?用transport_id和无线ADB精准区分

Android开发板ADB设备序列号重复?用transport_id和无线ADB精准区分

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/20 19:06:09 阅读更多 →
从 Prompt 到 Skills:AI 辅助编程的研发全生命周期实战指南

从 Prompt 到 Skills:AI 辅助编程的研发全生命周期实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/22 1:53:03 阅读更多 →

最新新闻

微信怎么圈所有人背后的性能优化陷阱与避坑实战

微信怎么圈所有人背后的性能优化陷阱与避坑实战

微信怎么圈所有人背后的性能优化陷阱与避坑实战 刚学会几个语法糖,就急着上手搭项目?别急,很多老手当年也栽过跟头。你写的代码跑得通,但一上量就卡死,这往往不是逻辑错,而是没懂底层性能优化逻辑。今天咱们不聊虚的,就盯着“微信怎么圈所有人”这个看…

2026/9/22 6:29:11 阅读更多 →
3步搞定微信桌面版官方下载与微服务联动保姆级教程

3步搞定微信桌面版官方下载与微服务联动保姆级教程

3步搞定微信桌面版官方下载与微服务联动保姆级教程 还在为看了一堆教程还是不会写项目而头秃?别慌,这篇保姆级教程专治各种“看着会,上手废”。很多应届生刚入职,拿着简历上写的“熟悉微服务架构”,真让他把IM消息推送和桌面端联动跑通,直接卡壳。今…

2026/9/22 6:29:11 阅读更多 →
一文搞懂升级访问:告别教程依赖,3步写出可上线代码

一文搞懂升级访问:告别教程依赖,3步写出可上线代码

一文搞懂升级访问:告别教程依赖,3步写出可上线代码 看了一堆教程还是不会写项目?别急着骂自己笨,这真不怪你。 很多老手都栽过跟头:照着视频敲代码能跑,换个需求就抓瞎,特别是涉及 升级访问…

2026/9/22 6:28:11 阅读更多 →
tennis怎么读:从音标到发音肌肉记忆,3步搞定发音难题

tennis怎么读:从音标到发音肌肉记忆,3步搞定发音难题

tennis怎么读:从音标到发音肌肉记忆,3步搞定发音难题 刚拿到网球拍,或者刚被朋友拉去打球,结果在记分牌前卡壳了?明明知道是“网球”,但张嘴想报分或者交流时,那个“Tennis”到底读 /ˈtenɪs/ 还是 /ˈtenɪs/…

2026/9/22 6:28:11 阅读更多 →
面试必问:3步吃透p2p网络电视源码架构

面试必问:3步吃透p2p网络电视源码架构

面试必问:3步吃透p2p网络电视源码架构 官方文档翻了三遍还是云里雾里?别急,p2p网络电视的底层逻辑其实没那么玄乎。 很多后端面试官喜欢拿这个问,因为能看出你对网络协议和性能优化的理解。…

2026/9/22 6:28:11 阅读更多 →
3招搞定qq假视频美女识别,性能优化让处理速度提升10倍

3招搞定qq假视频美女识别,性能优化让处理速度提升10倍

3招搞定qq假视频美女识别,性能优化让处理速度提升10倍 配置环境就卡半天,是不是你也遇到过这种情况?刚下载完依赖,运行脚本时内存直接飙到90%,处理一个qq假视频美女的样本集要等上半小时,CPU风扇狂转却不见进度条走动。这种低效的工作流,…

2026/9/22 6:27:10 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/22 4:32:41 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/22 4:38:57 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/21 4:51:05 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →