资讯详情

资讯详情

Bluebird TimeoutError 完全指南:超时错误的创建原理、`.timeout` 协作机制与实战捕获

后端【免费下载链接】bluebird:bird: :zap: Bluebird is a full featured promise library with unmatched performance.项目地址https://gitcode.com/gh_mirrors/bl/bluebird点击查看免费下载导读TimeoutError是 Bluebird 内置的专用错误类型用于信号化操作超时当某个 Promise 在限定的毫秒数内既未完成也未失败时Bluebird 的.timeout会以TimeoutError或你指定的自定义错误作为拒绝原因将该 Promise 拒绝。本文以 timeouterror.md 为核心结合 src/errors.js、src/timers.js、src/promise.js 与 test/mocha/timers.js 的源码与测试为你讲透它的构造方式、默认消息、跨库副本一致性、与超时取消机制的底层协作以及在实际代码中捕获与调试超时错误的完整方案。TimeoutError 是什么TimeoutError是 Bluebird 内置错误类型家族中的一员。Bluebird 提供了一组开箱即用的内置错误类型详见 built-in-error-types.md其中包括CancellationError取消操作信号TimeoutError超时操作信号OperationalError可操作错误如 I/O 失败AggregateError聚合错误其中TimeoutError的语义非常明确——它表示一个操作超出了允许的时间限制。它不携带业务数据只负责告诉调用方太慢了我已经等不下去了。构造函数签名与消息规则原文档给出的构造签名是new TimeoutError(String message) - TimeoutError构造函数接受一个字符串message作为第一个参数该字符串会成为错误对象的.message属性。从 src/errors.js 的源码可以看到TimeoutError由统一的subError工厂函数创建function subError(nameProperty, defaultMessage) { function SubError(message) { if (!(this instanceof SubError)) return new SubError(message); notEnumerableProp(this, message, typeof message string ? message : defaultMessage); notEnumerableProp(this, name, nameProperty); if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } else { Error.call(this); } } inherits(SubError, Error); return SubError; } var TimeoutError subError(TimeoutError, timeout error);这里有三个关键实现细节消息缺省回退subError将message设置为传入的字符串如果传入的不是字符串则回退为默认消息。TimeoutError的默认消息是timeout error即subError(TimeoutError, timeout error)的第二个参数。name属性固定为TimeoutError且与message一样通过notEnumerableProp设置为不可枚举保证错误序列化与遍历时的干净行为。继承关系inherits(SubError, Error)使TimeoutError是原生Error的子类因此instanceof Error、e.stack、e.message等原生行为全部可用在有Error.captureStackTrace的环境现代 V8下会捕获完整堆栈。注意工厂函数中的容错写法if (!(this instanceof SubError)) return new SubError(message);这意味着即使你忘了new关键字直接调用TimeoutError(msg)它也会返回一个正常的TimeoutError实例。默认消息常量在 src/constants.js 中定义了超时默认消息常量CONSTANT(TIMEOUT_ERROR, operation timed out);这就是当你使用.timeout(ms)且不提供任何自定义消息时最终注入到TimeoutError中的消息文本operation timed out与构造函数自身的默认值timeout error是两套机制前者由 src/timers.js 显式传入后者仅是构造时的兜底。如何获取 TimeoutError 引用TimeoutError不是全局变量需要通过 Promise 构造函数访问。在 src/promise.js 中完成挂载Promise.TimeoutError errors.TimeoutError;因此典型获取方式为var Promise require(bluebird); var TimeoutError Promise.TimeoutError;文档还特别指出所有内置错误类型在 Bluebird 的多个副本之间保持同一身份。这一点在 src/errors.js 中有专门实现——Bluebird 会将错误类型挂到Error[BLUEBIRD_ERRORS]这个不可写、不可枚举、不可配置的冻结对象上如果某个副本发现该属性已存在就直接复用已有类型而不是重新创建var errorTypes Error[BLUEBIRD_ERRORS]; if (!errorTypes) { errorTypes Objectfreeze({ CancellationError: CancellationError, TimeoutError: TimeoutError, OperationalError: OperationalError, RejectionError: OperationalError, AggregateError: AggregateError }); es5.defineProperty(Error, BLUEBIRD_ERRORS, { value: errorTypes, writable: false, enumerable: false, configurable: false }); }这意味着即使项目中同时加载了多个 Bluebird 副本例如通过不同依赖间接引入你在.catch(Promise.TimeoutError, ...)中的类型匹配依然可靠不会因为此副本的 TimeoutError 非彼副本的 TimeoutError而漏捕。相应的多副本一致性测试可参考 test/mocha/multiple-copies.js 与 test/mocha/bluebird-multiple-instances.js。TimeoutError 与.timeout的协作机制原文档明确TimeoutError被用作.timeout的自定义取消原因。先看.timeout的 API.timeout( int ms, [String messageoperation timed out] ) - Promise.timeout( int ms, [Error error] ) - Promise两种签名分别允许传字符串自定义错误消息传一个 Error 实例作为完整的拒绝原因此时不再创建TimeoutError。底层实现afterTimeout在 src/timers.js 中超时触发时的核心逻辑是afterTimeoutvar afterTimeout function (promise, message, parent) { var err; if (typeof message ! string) { if (message instanceof Error) { err message; } else { err new TimeoutError(TIMEOUT_ERROR); } } else { err new TimeoutError(message); } util.markAsOriginatingFromRejection(err); promise._attachExtraTrace(err); promise._reject(err); if (parent ! null) { parent.cancel(); } };可以看到完整的分支决策调用方式message 类型拒绝原因.timeout(100)未传undefinednew TimeoutError(operation timed out).timeout(100, custom message)字符串new TimeoutError(custom message).timeout(100, someError)Error 实例直接使用someError本身不创建TimeoutError此外afterTimeout还做了两件重要的事util.markAsOriginatingFromRejection(err)与promise._attachExtraTrace(err)将错误标记为源于拒绝并附加额外追踪信息这是 Bluebird 长堆栈追踪long stack traces能力的一部分parent.cancel()超时发生后会尝试取消父 Promise——这正是TimeoutError被称为自定义取消原因的原因。不过取消行为受 Promise.config 的cancellation选项控制详见下文。计时与清理HandleWrapperPromise.prototype.timeoutsrc/timers.js通过setTimeout建立计时器并用HandleWrapper包装句柄var handleWrapper new HandleWrapper(setTimeout(function timeoutTimeout() { if (ret.isPending()) { afterTimeout(ret, message, parent); } }, ms));若原 Promise 在ms毫秒内先完成/失败则通过successClear/failureClear回调clearTimeout掉计时器避免泄漏src/timers.js若在超时时刻原 Promise 仍处于 pendingret.isPending()为真才触发afterTimeout启用 cancellation 时ret._setOnCancel(handleWrapper)保证取消该派生 Promise 时同步清掉计时器而HandleWrapper.prototype._resultCancelled负责真正的clearTimeoutsrc/timers.js。对应的清理行为在 test/mocha/timers.js 中有专门测试无论 Promise 最终 fulfilled 还是 rejected都会以正确的句柄类型调用clearTimeout。实战如何在代码中捕获 TimeoutError原文档给出的经典示例文件读取超时场景var Promise require(bluebird); var fs Promise.promisifyAll(require(fs)); fs.readFileAsync(huge-file.txt).timeout(100).then(function(fileContents) { // 100ms 内读取完成 }).catch(Promise.TimeoutError, function(e) { console.log(could not read file within 100ms); });要点拆解Promise.promisifyAll(require(fs))把 Node 回调风格的fs变成 Promise 风格readFileAsync详见 promisification.md.timeout(100)给读取操作加上 100ms 的时限.catch(Promise.TimeoutError, ...)是 Bluebird 的谓词捕获只有拒绝原因是TimeoutError实例时才会进入该分支其他错误会继续往下传播。关于谓词捕获语法可参考 catch.md。自定义消息与自定义错误// 自定义消息 somePromise.timeout(2000, 数据库查询超过 2 秒).catch(Promise.TimeoutError, function(e) { console.log(e.message); // 数据库查询超过 2 秒 }); // 完全自定义错误对象不再创建 TimeoutError var customError new Error(gateway timeout); somePromise.timeout(2000, customError).caught(function(e) { assert(e customError); // true });第二条用法在 test/mocha/timers.js 中有测试验证当传入 Error 实例时捕获到的就是同一个对象。超时与取消的联动启用 cancellation 后Promise.config({cancellation: true})一旦超时发生父 Promise 会被取消其后续的.then回调不会再执行。测试 test/mocha/timers.js 验证了单个消费者场景下超时后父 Promise 被取消其后的回调不会执行存在多个消费者时如p被多处.then使用父 Promise 不会被取消避免误伤其他消费方。Promise.config({cancellation: true}); var p slowOperation(); // 22ms 后才完成 p.timeout(11).thenReturn(10).catch(Promise.TimeoutError, function(e) { // 11ms 超时p 被取消slowOperation 的后续逻辑不再执行 });捕获超时后如何区分与调试TimeoutError的实例具备以下可观测属性由 src/errors.js 的实现保证e.name TimeoutErrore.message默认operation timed out由 src/constants.js 的TIMEOUT_ERROR常量注入或你指定的自定义消息e instanceof Error与e instanceof Promise.TimeoutError均为true在支持Error.captureStackTrace的环境中带有完整堆栈且经过_attachExtraTrace附加了异步调用链信息便于定位是哪一个调用点上的超时。因此调试时可以直接打印promise.timeout(500).catch(Promise.TimeoutError, function(e) { console.error(超时, e.name, -, e.message); console.error(e.stack); });与相近概念的关系TimeoutErrorvs 普通Error.timeout的第二参数传入Error实例时拒绝原因就是该实例本身此时无法用Promise.TimeoutError谓词捕获只有未传或传字符串时才会产生TimeoutError。TimeoutErrorvs CancellationErrorTimeoutError用于时间限制到期CancellationError用于主动取消但超时发生时会调用parent.cancel()因此启用 cancellation 时超时往往会连锁触发父链路上的取消信号。跨库副本一致性TimeoutError等错误类型被冻结挂载在Error[BLUEBIRD_ERRORS]上src/errors.js保证多副本场景下谓词捕获依然有效。小结TimeoutError是 Bluebird 处理操作超时这一核心异常场景的标准信号构造方式为new TimeoutError(String message)name恒为TimeoutError缺省消息为timeout errorsrc/errors.js在.timeout(ms, message?)中未指定或指定字符串时产生TimeoutError默认消息为operation timed out指定 Error 实例则直接复用该实例src/timers.js通过Promise.TimeoutError引用配合.catch(Promise.TimeoutError, ...)谓词捕获实现精确、优雅的超时错误处理超时会触发父 Promise 取消受 cancellation 开关控制并有完善的计时器清理机制防止泄漏。掌握TimeoutError你就掌握了 Bluebird 超时控制这一高频场景的完整链路从错误构造、默认消息到.timeout的内部协作再到谓词捕获与取消联动全部可以在 docs/docs/api/timeouterror.md 的 API 定义之上通过 src/errors.js 与 src/timers.js 的源码以及 test/mocha/timers.js 的测试用例获得代码级验证。赞分享后端【免费下载链接】bluebird:bird: :zap: Bluebird is a full featured promise library with unmatched performance.项目地址https://gitcode.com/gh_mirrors/bl/bluebird点击查看免费下载相关推荐es-toolkit TimeoutError 完全指南超时错误的定义、抛出与捕获实践es toolkit TimeoutError 完全指南超时错误的定义、抛出与捕获实践 TimeoutError 是 es toolkit 提供的专用错误类前端后端Bluebird .timeout() 完全指南为 Promise 设置超时上限与自定义超时错误Bluebird .timeout 完全指南为 Promise 设置超时上限与自定义超时错误 导读 .timeout 是 Bluebird 中用于给任意 Pr后端Bluebird OperationalError 完全指南显式拒绝错误的识别、捕获与源码原理Bluebird OperationalError 完全指南显式拒绝错误的识别、捕获与源码原理 导读 OperationalError 是 Bluebir后端上一篇CANN/Ascend C SIMT线程组划分下一篇PersonaLive直播案例虚拟偶像如何用AI实现表情同步创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →