Bluebird `.timeout()` 完全指南:为 Promise 设置超时上限与自定义超时错误

📅 发布时间:2026/9/20 12:44:27
Bluebird `.timeout()` 完全指南:为 Promise 设置超时上限与自定义超时错误
后端【免费下载链接】bluebird:bird: :zap: Bluebird is a full featured promise library with unmatched performance.项目地址https://gitcode.com/gh_mirrors/bl/bluebird点击查看免费下载导读.timeout()是 Bluebird 中用于给任意 Promise 追加“最晚完成时间”的核心方法只要目标 Promise 在指定毫秒数内没有进入 fulfilled 或 rejected 状态返回的新 Promise 就会被立即拒绝拒绝原因是内置的Promise.TimeoutError或你自定义的错误。本文以官方 API 文档 docs/docs/api/timeout.md 为骨架结合 src/timers.js 的底层实现与 test/mocha/timers.js 的测试用例完整讲解两种调用签名、自定义消息与错误对象、超时后的取消行为、计时器清理机制以及配套的TimeoutError类型读完即可在自己的项目中安全地给文件读取、网络请求等不确定操作加上超时保护。.timeout()的两种调用签名根据官方 API 文档.timeout()支持两种签名区别只在于第二个参数的类型.timeout( int ms, [String messageoperation timed out] ) - Promise.timeout( int ms, [Error error] ) - Promise第一个参数ms是超时毫秒数整数必填第二个参数可选传入String时超时后拒绝原因是new TimeoutError(message)即自定义超时错误消息传入Error实例时超时后直接以该错误对象作为拒绝原因不传时默认错误消息为operation timed out该字符串定义在 src/constants.js 的TIMEOUT_ERROR常量中。返回的新 Promise 会沿用原 Promise 的fulfillment 值或 rejection 原因如果原 Promise 在ms毫秒内正常完成那么.timeout()返回的 Promise 也以同样的值 fulfill 或同样的原因 reject行为与直接使用原 Promise 完全一致只有超时这一种情况下它才会以自己的方式拒绝。这一点在 src/timers.js 中体现为successClear与failureClear两个透传处理器——它们在透传值/原因的同时只负责清理底层定时器句柄。基础用法给异步操作加上超时保护文档给出的经典示例是为文件读取设置 100ms 超时var Promise require(bluebird); var fs Promise.promisifyAll(require(fs)); fs.readFileAsync(huge-file.txt).timeout(100).then(function(fileContents) { }).catch(Promise.TimeoutError, function(e) { console.log(could not read file within 100ms); });这里需要几个 Bluebird 前置知识全部在仓库中有据可查Promise.promisifyAll是 promisify.js 提供的批量 Promise 化 API会给fs的所有方法生成以Async结尾的 Promise 版本readFileAsync即由此而来相关细节见 docs/docs/api/promise.promisifyall.mdPromise.TimeoutError是挂载在Promise构造函数上的内置错误类型由 src/promise.js 的Promise.TimeoutError errors.TimeoutError赋值其定义位于 src/errors.js.catch(Promise.TimeoutError, function(e){...})是 Bluebird 的类型化错误捕获catch_filter.js只有拒绝原因是TimeoutError实例时才会进入该分支普通读取错误如文件不存在会继续向下传播。测试 test/mocha/timers.js 从三个角度验证了基础行为见describe(timeout, ...)用例快速完成不做任何事Promise.delay(1).timeout(200)正常 fulfill不会误伤快速拒绝原样传递.timeout(200)后捕获到的错误error goodError即原 Promise 的拒绝原因被原样透传没有替换成超时错误确实超时才拒绝Promise.delay(1).timeout(10)因 10ms 小于 1ms 之后完成的延迟被Promise.TimeoutError拒绝。自定义超时错误字符串消息与 Error 对象文档指出“When using the first signature, you may specify a custom error message with themessageparameter.” 即第一种签名可用字符串自定义消息第二种签名则可直接传入一个 Error 对象。两者分别有对应的测试佐证// 字符串消息 Promise.delay(1) .timeout(10, custom) .caught(Promise.TimeoutError, function(e){ assert(/custom/i.test(e.message)); // e.message 包含 custom }); // Error 对象 var err Error(Testing Errors); Promise.delay(1) .timeout(10, err) .caught(function(e){ assert(e err); // 拒绝原因就是同一个对象 });从源码 src/timers.js 的afterTimeout函数可以看清底层分支逻辑若message是字符串则err new TimeoutError(message)若message是Error实例则直接复用该对象作为拒绝原因这正是测试中断言e err成立的原因若两者都不是比如传了null或未传则回退为new TimeoutError(TIMEOUT_ERROR)即默认消息operation timed out。此外afterTimeout还做了两件容易被忽略的事通过util.markAsOriginatingFromRejection(err)把该错误标记为“源于拒绝”并通过promise._attachExtraTrace(err)将错误附加到 Promise 的追踪链上——这两步与 Bluebird 的长堆栈追踪long stack traces机制配合能帮助你在开启调试时定位“超时发生前”的完整调用来源。超时后的取消行为cancellation 模式.timeout()与 Bluebird 的取消机制docs/docs/api/cancellation.md紧密相关。在 src/timers.js 的实现中Promise.prototype.timeout function (ms, message) { ms ms; var ret, parent; var handleWrapper new HandleWrapper(setTimeout(function timeoutTimeout() { if (ret.isPending()) { afterTimeout(ret, message, parent); } }, ms)); if (debug.cancellation()) { parent this.then(); ret parent._then(successClear, failureClear, undefined, handleWrapper, undefined); ret._setOnCancel(handleWrapper); } else { ret this._then(successClear, failureClear, undefined, handleWrapper, undefined); } return ret; };要点如下定时器回调timeoutTimeout中先用ret.isPending()判断返回的 Promise 是否仍处于 pending 状态只有仍 pending 才会触发超时拒绝——这是“先完成者胜”语义的保证当通过Promise.config({cancellation: true})开启取消支持见 docs/docs/api/promise.config.md后parent this.then()会创建一个内部父 Promise 快照超时触发时afterTimeout(ret, message, parent)会调用parent.cancel()取消原 Promise 链从而让底层操作如仍在进行中的 I/O 回调尽早释放HandleWrapper包装了setTimeout返回的句柄并通过_setOnCancel(handleWrapper)把句柄与返回 Promise 的取消操作绑定一旦返回的 Promise 被取消_resultCancelled会立即clearTimeout清理定时器避免定时器泄漏。测试 test/mocha/timers.js 对取消行为做了正反两面的验证有且仅有一个消费者时p.timeout(11)超时后原 Promise 链p被取消其.then(...)回调didNotExecute保持为true父 promise 确实被取消了存在多个消费者时如果同一个原 Promise 还被其他分支派生过var derived p.then(...)则超时取消不会波及共享的其他消费者derived仍会正常执行——这是取消传播的“引用计数”保护语义。计时器句柄的可靠清理.timeout()返回的 Promise 一旦完成无论 fulfill 还是 reject底层setTimeout都必须被清除否则在 Node.js 等环境中会造成句柄悬挂。这一机制由successClear/failureClear两个处理器承担src/timers.jsfunction successClear(value) { clearTimeout(this.handle); return value; } function failureClear(reason) { clearTimeout(this.handle); throw reason; }this是注入的handleWrapperthis.handle即setTimeout返回的句柄二者在透传结果/原因的同时执行clearTimeout保证超时定时器不会在 Promise 提前完成的情况下继续悬挂。test/mocha/timers.js 中的 “timer handle clearouts” 测试组专门验证了这一点测试通过替换全局clearTimeout来捕获调用参数分别断言快速 fulfillPromise.delay(1).timeout(10000)和快速 rejectsetTimeout(reject, 10)后接.timeout(10000)两种场景下clearTimeout都以正确的句柄类型被调用从侧面证实了定时器在完成路径上必然被清理。配套类型TimeoutError.timeout()默认使用的拒绝原因是 Bluebird 内置的TimeoutError其构造签名见 docs/docs/api/timeouterror.mdnew TimeoutError(String message) - TimeoutErrorTimeoutError表示“某个操作已超时”专门作为.timeout()的默认取消/拒绝原因使用。在源码层面它由 src/errors.js 的subError(TimeoutError, timeout error)工厂函数创建继承自原生Error未传 message 时的默认消息为timeout error它与其他内置错误类型CancellationError、OperationalError、AggregateError、Warning一起被冻结并注册到Error[BLUEBIRD_ERRORS]src/errors.js这一设计确保了同一进程中多份 Bluebird 拷贝共享相同的错误类型跨库实例捕获时类型判断依然成立你通过Promise.TimeoutErrorsrc/promise.js即可访问该类型用于.catch(Promise.TimeoutError, handler)的类型化捕获或用于instanceof判断。常见问题与最佳实践1. 超时后原 Promise 还在执行吗默认未开启 cancellation情况下.timeout()只是让“结果”超时作废原 Promise 底层的异步操作仍会继续运行只是其结果不再被消费。若希望超时后尽早中止底层操作请先调用Promise.config({cancellation: true})docs/docs/api/promise.config.md并注意多消费者场景下取消不会波及共享分支见上文测试。2. 如何区分“超时失败”与“业务失败”推荐使用类型化捕获让两种失败各走各的分支op().timeout(5000).then(function(value) { // 5 秒内完成 }).catch(Promise.TimeoutError, function(e) { // 5 秒未完成e.message 为自定义消息或 operation timed out }).catch(function(e) { // 其他业务错误 });3.ms参数的类型处理实现中ms mssrc/timers.js做了隐式数值转换因此传入字符串形式的数字如100也会被当作毫秒数处理但请始终传入正整数以避免语义混乱。4. 超时与Promise.delay组合使用.timeout()与Promise.delaydocs/docs/api/promise.delay.md、src/timers.js同属 Timers 家族docs/docs/api/timers.md常搭配使用Promise.delay(ms, value)是“延迟后再完成”.timeout(ms)是“超过时限即失败”二者一缓一急共同构成对异步时序的完整控制能力。小结.timeout()以极小的 API 面提供了完整的 Promise 超时语义两种签名覆盖“自定义消息”与“自定义错误对象”两种需求底层通过setTimeoutisPending()检查保证先完成者胜通过successClear/failureClear保证计时器句柄在任何完成路径上都被清理配合cancellation: true时还能在超时后主动取消原 Promise 链。其默认错误类型TimeoutError可经由Promise.TimeoutError做类型化捕获。理解 src/timers.js 的实现与 test/mocha/timers.js 的测试能让你在文件 I/O、网络请求、数据库访问等场景中放心地把超时控制交给 Bluebird。赞分享后端【免费下载链接】bluebird:bird: :zap: Bluebird is a full featured promise library with unmatched performance.项目地址https://gitcode.com/gh_mirrors/bl/bluebird点击查看免费下载相关推荐Soul Signature 技术规格解析RuView 基于 RVF 图结构的七通道多模态被动电磁生物特征签名Soul Signature 技术规格解析RuView 基于 RVF 图结构的七通道多模态被动电磁生物特征签名 导读 Soul Signature 是 Ru后端Bluebird 定时器 API 深度指南.delay 延迟与 .timeout 超时机制全解析Bluebird 定时器 API 深度指南 .delay 延迟与 .timeout 超时机制全解析 导读 在 Bluebird 中Timers 指一组用后端Hono 如何用 timeout 中间件为耗时请求设置超时并返回自定义异常Hono 如何用 timeout 中间件为耗时请求设置超时并返回自定义异常 如果某个 Hono 请求处理函数需要等待较慢的下游数据库、第三方 API 等后端Web框架上一篇Wail2Ban - 监控并自动阻止恶意网络行为的工具下一篇HaE高级搜索功能快速找到你需要的HTTP消息创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考