恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Bluebird Promise.props 使用指南:并行等待对象属性与 Map 键值对中的 Promise
首页
资讯中心
/
Bluebird Promise.props 使用指南:并行等待对象属性与 Map 键值对中的 Promise
Bluebird Promise.props 使用指南:并行等待对象属性与 Map 键值对中的 Promise
发布时间:2026/9/20 19:06:03
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),仅供参考