ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Bluebird Promise 链式抛错指南:.throw() 与 .thenThrow() 的用法、绑定时机与底层实现

Bluebird Promise 链式抛错指南:.throw() 与 .thenThrow() 的用法、绑定时机与底层实现 Bluebird Promise 链式抛错指南.throw() 与 .thenThrow() 的用法、绑定时机与底层实现【免费下载链接】bluebird:bird: :zap: Bluebird is a full featured promise library with unmatched performance.项目地址: https://gitcode.com/gh_mirrors/bl/bluebird导读本文聚焦 Bluebird 中用于在 Promise 链中主动触发拒绝rejection的便捷方法.throw(reason)及其别名.thenThrow(reason)。在实际开发中当异步流程走到某一步需要中断并抛出一个错误时.throw()提供了一种比Promise.reject()或throw语句更贴合链式风格的写法。读完本文你将掌握.throw()的 API 语义、reason求值时机带来的绑定时间陷阱、其与.return()/.catchThrow()的配对关系以及该方法在 src/direct_resolve.js 中的真实实现原理。.throw()的 API 签名与语义根据 官方 API 文档.throw()的完整签名为.throw(any reason) - Promise .thenThrow(any reason) - Promise其中reason是任意类型的值——可以是Error实例也可以是字符串、数字、布尔值乃至undefined。该方法返回一个新的 Promise该 Promise 会被以reason作为拒绝原因进入 rejected 状态。它本质上是一个便捷方法convenience method等价于下面这段代码.then(function() { throw reason; });也就是说.throw(reason)只在**前一个 Promise 兑现fulfilled**时触发抛出逻辑如果前一个 Promise 已经拒绝则该拒绝会原样向后传递.throw()不会拦截或吞掉它。这一点对理解它的行为至关重要它是一个兑现时抛错的处理函数而不是一个错误处理器。别名.thenThrow.thenThrow是.throw的别名二者完全等价。文档明确说明提供该别名是为了兼容早期 ECMAScript 版本——在部分旧标准中throw属于保留字作为属性名promise.throw(...)使用时存在兼容性问题因此提供thenThrow作为安全替代。这一点在 Bluebird 的 安装文档 中也有呼应.throw()对应.thenThrow()。同时从其他库迁移指南 还提到来自其他 Promise 库的.thenReject()语义可以对应到 Bluebird 的.throw()或thenThrow()说明该方法在设计上承担了链式主动拒绝的通用职责。绑定时间binding time陷阱.throw()与.return()一样存在一个关于reason绑定时间的约束。reason是在调用.throw(reason)的那一刻被求值并绑定到内部状态的而不是在 Promise 兑现后再从闭包中读取。这一点在.return文档 中有明确的对应说明.throw文档指出其适用同样的限制。下面这段看起来对、实际上错的代码可以说明问题function getData() { var data; return query().then(function(result) { data result; }).throw(data); }由于.throw(data)是在query()返回的 Promise 被then处理之前同步执行的此刻data仍是undefined因此最终抛出的拒绝原因是undefined而不是查询结果。类似地如果你试图在异步回调里给外部变量赋值、再在链尾.throw(该变量)同样会踩到值为undefined的坑。提示reason本身是函数参数在.throw(reason)调用时同步求值而抛出的动作发生在异步链的后续阶段。这二者之间的时间差正是绑定时间陷阱的根源。正确用法示例主动中断异步链路.throw()最典型的场景是在条件判断后主动让链走向失败分支。下面的例子展示了一个带鉴权检查的下载流程token 缺失时立即返回一个已拒绝的 Promisevar Promise require(bluebird); var request Promise.promisifyAll(require(request)); function download(url, token) { if (!token) { return Promise.resolve().thenThrow(new Error(missing auth token)); } return request.getAsync(url, { headers: { Authorization: Bearer token } }); } download(https://api.example.com/data, null) .then(function(res) { console.log(got data:, res.body); }) .catch(function(err) { console.error(download failed:, err.message); // missing auth token });也可以配合Promise.resolve()/Promise.delay()等已兑现的 Promise把延迟抛错写进异步链中。例如在 disposer 文档 的使用示例中就出现了.thenThrow(err)的写法用于在资源释放流程中主动抛出错误。源码实现剖析为什么.throw()是同步绑定的.throw()与.thenThrow()的实现位于 src/direct_resolve.jsfunction returner() { return this.value; } function thrower() { throw this.reason; } Promise.prototype[return] Promise.prototype.thenReturn function (value) { if (value instanceof Promise) value.suppressUnhandledRejections(); return this._then( returner, undefined, undefined, {value: value}, undefined); }; Promise.prototype[throw] Promise.prototype.thenThrow function (reason) { return this._then( thrower, undefined, undefined, {reason: reason}, undefined); };从源码结构可以清楚地看到几个关键设计reason被立即存入对象字面量{reason: reason}作为_then的receiver参数传入。这就是绑定时间的代码级证据——值在方法调用瞬间被捕获之后 Promise 链如何变化都不会影响这个已保存的值。thrower是独立的内部函数function thrower() { throw this.reason; }它通过this.reason从 receiver 中取出当初绑定的值。因为 JS 函数参数按值传递基本类型和对象引用都在调用时被固定下来。.throw与.return的实现完全对称一个返回this.value一个抛出this.reason共用了同一套_then调度机制这正是文档把它们并列说明的原因。_then的第二个参数是undefinedrejection handler 为空这印证了前文的语义——.throw()不处理拒绝前一个 Promise 的拒绝会原样向后传递。底层机制同步 throw 如何变成异步 rejection.throw()内部的thrower抛出的异常最终通过 src/promise.js 的_settlePromiseFromHandler被转化为拒绝var x tryCatch(handler).call(receiver, value); if (x errorObj) { promise._rejectCallback(x.e, false); } else { promise._resolveCallback(x); }这里tryCatch捕获了 handler 同步抛出的异常并包装成errorObj随后调用_rejectCallback(x.e, false)把异常值作为拒绝原因传递下去。也就是说thrower里那个朴素的throw经过 Bluebird 内部调度后变成了下一个 Promise 的 rejection整个过程对使用者透明。这也是在 Promise 执行器中/处理函数中抛出异常会被捕获为拒绝这一 Promise 语义的底层证据。测试验证覆盖基本类型、对象与边界情况仓库测试文件 test/mocha/direct_resolving.js 对.thenThrow做了相当全面的覆盖基本类型L147-L153null、undefined、字符串、数字、布尔值均可作为拒绝原因抛出对象类型L155-L160普通对象、函数、内建函数如Array、内建对象如Math也全部支持不吞错测试L162-L169对一个已被拒绝的 Promise 调用.thenThrow(3)最终收到的仍是原始错误e而不是3证明.throw只在兑现时生效、绝不会覆盖既有的拒绝抛undefined边界L235-L241对应 issue gh-627Promise.bind(42).thenThrow(undefined)可以合法地以undefined作为拒绝原因且能被catchThrow()正确接住。测试中的throwValue辅助函数L32-L38展示了标准的断言模式function throwValue(value) { helpers.testFulfilled(void 0, function(promise) { return promise.thenThrow(value).then(assert.fail, function(v) { assert(v value); }); }); }即对一个已兑现的 Promise 调用thenThrow(value)断言其最终以value被拒绝。与配对 API 组合.catchThrow()/.catchReturn().throw()并不孤单它与.catchThrow()、.catchReturn()构成了一个完整的链上抛出/返回家族均定义在 src/direct_resolve.js 中Promise.prototype.catchThrow function (reason) { if (arguments.length 1) { return this._then( undefined, thrower, undefined, {reason: reason}, undefined); } else { var _reason arguments[1]; var handler function() {throw _reason;}; return this.caught(reason, handler); } };.catchThrow(reason)在拒绝时抛出reason即把拒绝原因替换为指定值.catchThrow(ErrorType, reason)支持传入一个错误类型做过滤仅当匹配时才抛出新原因否则原错误继续传递.catchReturn(value)拒绝时改为兑现为value。它们与.throw()的区别仅在于挂载点不同.throw()的 handler 挂在 fulfillment 分支_then第一个参数.catchThrow()挂在 rejection 分支_then第二个参数。catchThrow/catchReturn是 Bluebird 3 中新增的能力见 new-in-bluebird-3 文档 及 catchThrow 文档、catchReturn 文档。典型组合用法——校验失败时抛出指定错误Promise.reject(new Error(bad input)) .catchThrow(TypeError, new Error(input must be a string)) .catch(function(e) { // 收到的是 input must be a string });使用要点小结.throw(reason)等价于.then(function() { throw reason; })只在前一个 Promise 兑现时触发reason在调用时刻求值并绑定不要在异步回调之后再引用外部变量期望它能被延迟读取可以抛出任意值包括undefined不一定必须是Error实例但生产代码仍建议抛Error以保留堆栈信息已拒绝的 Promise 调用.throw()不会吞掉原错误.thenThrow与.throw完全等价旧 ES 环境建议使用前者需要在拒绝时抛新值请改用.catchThrow()需要兑现为指定值请参考.return()。从 API 参考索引 可以看到.throw被归入直接解析direct resolution一族 API与.return、.catchReturn、.catchThrow并列。理解这一族方法的对称设计就能在编写 Bluebird 链式流程时游刃有余地选择何时返回、何时抛出、何时替换拒绝原因。【免费下载链接】bluebird:bird: :zap: Bluebird is a full featured promise library with unmatched performance.项目地址: https://gitcode.com/gh_mirrors/bl/bluebird创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表