ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Bluebird PromiseInspection 完全指南:反射式 Promise 状态检查接口的源码级剖析

Bluebird PromiseInspection 完全指南:反射式 Promise 状态检查接口的源码级剖析 Bluebird PromiseInspection 完全指南反射式 Promise 状态检查接口的源码级剖析【免费下载链接】bluebird:bird: :zap: Bluebird is a full featured promise library with unmatched performance.项目地址: https://gitcode.com/gh_mirrors/bl/bluebird本文基于 promiseinspection.md 文档并结合 src/synchronous_inspection.js、src/promise.js、src/settle.js 等源码与 test/mocha/reflect.js 测试用例深入讲解 PromiseInspection 接口的定义、实现原理与实战用法。读完本文你将掌握如何通过value()、reason()、isFulfilled()等检查器安全地观察 Promise 的最终状态并能在不捕获异常的前提下实现等所有 Promise 结束再统一处理settleAll / settleProps等场景。什么是 PromiseInspection在 Promise 编程中一个常见的痛点是当你想等待一组 Promise 全部结束无论成功还是失败再做统一处理时用Promise.all会在第一个拒绝时立即短路导致你拿不到每个 Promise 各自的结果。Bluebird 为此定义了一个名为PromiseInspection的接口interface——它是一个只读的状态快照不携带任何回调逻辑只负责回答两个问题这个 Promise 现在处于什么状态状态对应的值是什么原文档 promiseinspection.md 给出了该接口的完整定义interface PromiseInspection { any reason() any value() boolean isPending() boolean isRejected() boolean isFulfilled() boolean isCancelled() }文档明确说明该接口不仅由Promise实例自身实现也由.reflect()方法返回的PromiseInspection结果对象实现。也就是说在 Bluebird 中检查一个 Promise 的状态 与 检查一个 Promise 实例 是同一套方法体系。方法逐一解析六个检查器的语义与返回值value() —— 获取兑现值value()返回该 inspection 所代表 Promise 的兑现值fulfillment value。关键约束源码级证据在 src/synchronous_inspection.js 中var value PromiseInspection.prototype.value function () { if (!this.isFulfilled()) { throw new TypeError(INSPECTION_VALUE_ERROR); } return this._settledValue(); };如果当前状态不是isFulfilled()调用value()会抛出TypeError其消息定义在 src/constants.jsCONSTANT(INSPECTION_VALUE_ERROR, cannot get fulfillment value of a non-fulfilled promise\n\n\ See http://goo.gl/MqrFmX\n);reason() —— 获取拒绝原因reason()返回拒绝原因rejection reason且该方法有一个别名error源码中PromiseInspection.prototype.error PromiseInspection.prototype.reason。与value()对称只有isRejected()为真时才允许调用否则抛出INSPECTION_REASON_ERRORcannot get rejection reason of a non-rejected promise见 src/constants.js。var reason PromiseInspection.prototype.error PromiseInspection.prototype.reason function () { if (!this.isRejected()) { throw new TypeError(INSPECTION_REASON_ERROR); } return this._settledValue(); };isPending() / isFulfilled() / isRejected() / isCancelled()这四个布尔方法分别判断是否仍处于等待中、是否已兑现、是否已拒绝、是否已取消。语义上是互斥且完备的——任意时刻一个 Promise 恰好处于其中一种终态或仍在 pending。值得注意的是源码中还额外提供了一个未写进接口定义但真实存在的方法isResolved()兑现或拒绝即为 resolvedvar isResolved PromiseInspection.prototype.isResolved function () { return (this._bitField IS_REJECTED_OR_FULFILLED) ! 0; };源码级原理位字段bitField驱动的状态机PromiseInspection 的实现之所以能如此轻量只读快照、零回调开销是因为 Bluebird 将 Promise 的状态编码在 Promise 实例的_bitField整型位字段中。理解这一点就理解了整个接口的性能根基。构造过程在 src/synchronous_inspection.js 中function PromiseInspection(promise) { if (promise ! undefined) { promise promise._target(); this._bitField promise._bitField; this._settledValueField promise._isFateSealed() ? promise._settledValue() : undefined; } else { this._bitField 0; this._settledValueField undefined; } }要点构造时先调用_target()穿透到链式目标Promise 可能 follow 另一个 Promise取目标对象的_bitField快照只有当命运已定_isFateSealed()时才拷贝_settledValue()否则值字段为undefined——因此pending 状态的 inspection 调value()必然抛错检查器从PromiseInspection被创建的那一刻起就是不可变的快照之后 Promise 再改变状态也不影响已生成的 inspection。位字段布局_bitField中与状态相关的位定义在 src/constants.js注释中给出了 32 位的完整布局说明//Layout for ._bitField //[RR]XO GWFN CTBH IUDE LLLL LLLL LLLL LLLL //... CONSTANT(IS_FULFILLED, 0x2000000|0); CONSTANT(IS_REJECTED, 0x1000000|0); CONSTANT(WILL_BE_CANCELLED, 0x800000|0); CONSTANT(IS_CANCELLED, 0x10000|0); CONSTANT(IS_CANCELLED_OR_WILL_BE_CANCELLED, IS_CANCELLED | WILL_BE_CANCELLED) CONSTANT(IS_REJECTED_OR_FULFILLED, IS_REJECTED | IS_FULFILLED); CONSTANT(IS_REJECTED_OR_FULFILLED_OR_CANCELLED, IS_REJECTED | IS_FULFILLED | IS_CANCELLED);对应的位运算判定var isFulfilled PromiseInspection.prototype.isFulfilled function() { return (this._bitField IS_FULFILLED) ! 0; }; var isRejected PromiseInspection.prototype.isRejected function () { return (this._bitField IS_REJECTED) ! 0; }; var isPending PromiseInspection.prototype.isPending function () { return (this._bitField IS_REJECTED_OR_FULFILLED_OR_CANCELLED) 0; }; PromiseInspection.prototype.isCancelled function() { return (this._bitField IS_CANCELLED_OR_WILL_BE_CANCELLED) ! 0; };可以推断isPending的定义是既非 fulfilled、又非 rejected、也非 cancelled因此它是一个对三者取反的组合判断isCancelled则不仅覆盖已取消状态也覆盖即将被取消WILL_BE_CANCELLED的传播标记——这对应 Bluebird 的取消传播机制详见 cancellation.md。Promise 实例自身也实现该接口同步检查文档强调 This interface is implemented byPromiseinstances。在 src/synchronous_inspection.js 中Promise.prototype上直接挂载了同名方法且统一先_target()再复用检查器逻辑Promise.prototype.isPending function() { return isPending.call(this._target()); }; Promise.prototype.isRejected function() { return isRejected.call(this._target()); }; Promise.prototype.isFulfilled function() { return isFulfilled.call(this._target()); }; Promise.prototype.isResolved function() { return isResolved.call(this._target()); }; Promise.prototype.value function() { return value.call(this._target()); }; Promise.prototype.reason function() { var target this._target(); target._unsetRejectionIsUnhandled(); return reason.call(target); };两个值得注意的细节Promise.prototype.reason()会先调用_unsetRejectionIsUnhandled()即读取拒绝原因会同时清除该拒绝的未处理标记避免 Bluebird 将这次读取误判为未处理的拒绝而触发unhandledRejection报告Promise.prototype.value()和reason()在非对应状态下同样会抛TypeError——例如对一个尚未兑现的 Promise 直接调用.value()是非法的。这一能力让 Bluebird 支持同步状态检查相关文档见 synchronous-inspection.md在then回调之外、不依赖回调时序的前提下用if (promise.isFulfilled()) { promise.value() }的方式读取结果。实战一.reflect() 与 settleAll —— 等所有 Promise 结束再处理PromiseInspection最主要的消费场景是.reflect()。文档 reflect.md 描述如下.reflect() - PromisePromiseInspectionThe.reflect()method returns a promise that is always successful when this promise is settled. Its fulfillment value is an object that implements the PromiseInspection interface and reflects the resolution of this promise.即.reflect()返回一个永远不会拒绝的 Promise无论原 Promise 最终兑现还是拒绝这个返回的 Promise 都会以 fulfilled 状态结束其兑现值就是一个PromiseInspection对象。它的实现位于 src/promise.js 与 src/promise.jsvar reflectHandler function() { return new Promise.PromiseInspection(this._target()); }; util.setReflectHandler(reflectHandler); Promise.prototype.reflect function () { return this._then(reflectHandler, reflectHandler, undefined, this, undefined); };可以看到reflectHandler同时被用作兑现回调与拒绝回调——因此无论原 Promise 走向哪个终态都会走同一个成功路径生成 inspection这正是永远成功的机制来源。settleAll 示例原文档给出了用.reflect()实现settleAll等待数组中所有 Promise 全部结束的完整示例var promises [getPromise(), getPromise(), getPromise()]; Promise.all(promises.map(function(promise) { return promise.reflect(); })).each(function(inspection) { if (inspection.isFulfilled()) { console.log(A promise in the array was fulfilled with, inspection.value()); } else { console.error(A promise in the array was rejected with, inspection.reason()); } });其中.each()是 Bluebird 的逐项遍历方法见 each.mdPromise.all(...).each(...)保证数组内任何一个 Promise 拒绝都不会导致整个流程中断你可以在回调里通过isFulfilled()/isRejected()分流处理每一项。settleProps 示例同样的思路可以作用于对象的属性like settleAll for an objects propertiesvar object { first: getPromise1(), second: getPromise2() }; Promise.props(Object.keys(object).reduce(function(newObject, key) { newObject[key] object[key].reflect(); return newObject; }, {})).then(function(object) { if (object.first.isFulfilled()) { console.log(first was fulfilled with, object.first.value()); } else { console.error(first was rejected with, object.first.reason()); } })这里先用reduce把每个属性值替换为其.reflect()后的结果再交给Promise.props见 props.md等待全部属性结束之后通过检查每个属性的 inspection 判断各自成败。实战二Promise.allSettled 与已废弃的 Promise.settle对于数组全量等待这个高频需求Bluebird 还提供了内建 API。在 src/settle.js 中SettledPromiseArray直接基于PromiseInspection构建结果数组SettledPromiseArray.prototype._promiseFulfilled function (value, index) { var ret new PromiseInspection(); ret._bitField IS_FULFILLED; ret._settledValueField value; return this._promiseResolved(index, ret); }; SettledPromiseArray.prototype._promiseRejected function (reason, index) { var ret new PromiseInspection(); ret._bitField IS_REJECTED; ret._settledValueField reason; return this._promiseResolved(index, ret); };由此派生出两个入口Promise.settle function (promises) { debug.deprecated(.settle(), .reflect()); return new SettledPromiseArray(promises).promise(); }; Promise.allSettled function (promises) { return new SettledPromiseArray(promises).promise(); };Promise.allSettled(promises)返回一个 Promise其兑现值为与输入等长的PromiseInspection数组每个元素对应输入数组在该位置的最终状态Promise.settle(promises)与allSettled行为一致但已废弃源码中显式标记debug.deprecated(.settle(), .reflect())应改用.reflect()或allSettled。因此如果你只是想拿一批PromiseInspection直接用Promise.allSettled([...])即可无需手动mapreflect。取消状态检查与边界约束isCancelled()是 PromiseInspection 接口中容易被忽略的一项。它对应 Bluebird 的取消机制需开启cancellation配置见 cancellation.md 与 promise.config.md。当 Promise 被取消时isCancelled()返回trueisPending()返回false因为isPending排除了 cancelled 状态位value()/reason()依然会抛错既不 fulfilled 也不 rejected。从源码可见isCancelled同时检查IS_CANCELLED与WILL_BE_CANCELLED两个位因此它反映的是已取消或将取消的传播状态。测试验证行为边界有据可依仓库测试文件 test/mocha/synchronous_inspection.js 与 test/mocha/reflect.js 完整验证了上述语义兑现检查ret.value() 10且ret.isFulfilled() true拒绝检查ret.reason() e且ret.isRejected() truepending 检查deferred.promise.isPending() true未 resolve 前异常边界对未兑现的 inspection 调用.value()应抛TypeError.value() of unfulfilled inspection should throw对未拒绝的 inspection 调用.reason()同理reflect 集成inspection.isFulfilled()/inspection.value() 1、inspection.isRejected()/inspection.reason() 2均通过断言。这些用例说明状态判断与取值强绑定、非法取值必抛错这是 PromiseInspection 设计上宁可抛错也不返回错误数据的严格契约。最佳实践小结优先用Promise.allSettled处理全部结束再统一处理的数组场景语义清晰且无需手动映射需要更精细的控制流时用.reflect()配合.each()、Promise.props可实现 settleAll / settleProps 等模式判断顺序先isFulfilled()/isRejected()/isCancelled()再调用value()/reason()避免触发TypeError同步检查有代价约束Promise.prototype.isFulfilled()等同步方法适用于已确定终态的场景对尚未 settled 的 Promise 调用value()/reason()会抛错不要依赖先查再取绕过异步时序不要使用已废弃的Promise.settle源码已标记其被.reflect()取代src/settle.js。相关文档导航接口定义与实现promiseinspection.md / src/synchronous_inspection.js.reflect()用法reflect.md同步检查指南synchronous-inspection.md取消机制cancellation.md / cancel.md配置开关promise.config.md全量 API 目录api-reference.md【免费下载链接】bluebird:bird: :zap: Bluebird is a full featured promise library with unmatched performance.项目地址: https://gitcode.com/gh_mirrors/bl/bluebird创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表