防止 ES6 Promise 吞下错误(不使用 .catch)
Posted
技术标签:
【中文标题】防止 ES6 Promise 吞下错误(不使用 .catch)【英文标题】:Prevent ES6 promises from swallowing errors (without using .catch) 【发布时间】:2017-05-24 23:19:36 【问题描述】:我不想为我使用的每一个承诺都输入 .catch。如果不这样做,由 Promise 引起的错误是非常无益的。
纯粹为了这个目的使用像 bluebird 这样的整个库让我感到不舒服。
【问题讨论】:
“你使用的每一个承诺”是什么意思?你只需要在链尾catch
errors。另外,“吞”是什么意思?如果你想防止这种情况发生,错误应该怎么办?
当使用大量的 Promise 时,将 .catch 放在每条链之后可能会让人筋疲力尽,而且一点也不干。编辑:吞咽我的意思是没有提供错误行或细节。默认情况下,这些错误应该有一个关联的处理程序,在我的情况下,我希望它们显示在我的节点控制台中。
除了省略.catch(function(e) …)
之外,还有其他方法可以使此类代码枯燥无味,这在任何地方都是一样的。你的许多连锁店是从哪里开始的?事件处理程序,路由处理程序?然后定义一个包装函数来安装处理程序并适当地处理返回的 Promise。如果您可以发布您的代码,我将能够写一个更具体的答案。
编写没有任何.catch()
处理程序的代码只是糟糕的代码。您不会全局诊断丢失的拒绝处理程序。这不是你如何编写好的代码。您不必在每个承诺上都加上.catch()
。尽管我在回答中解释了逻辑,但您应该在每个承诺链上都有一个。如果您是对我的回答投反对票的人,那么对于为您的情况提供正确的编程答案,我深表歉意。你不能按照你要求的方式去做,所以我已经描述了你应该如何处理拒绝。如果您不喜欢这样,那么我可以删除我的答案。
@jfriend00,没有注意到你在那里评论。我没有对你投反对票,但不同意这是糟糕的代码。我的目的只是拥有一个默认的错误处理程序,以防止出现许多相同的 .catch。在错误与程序功能或用户体验相关的地方,我有 .catchs 处理错误。然而,在某些情况下,我只想记录错误(到控制台、日志文件或错误数据库)。这没什么不好的:)
【参考方案1】:
为未处理的拒绝添加处理程序。您可以像这样处理它们一次,而不是一直复制粘贴 .catch 语句。
process.on( 'unhandledRejection', ( error, promise ) =>
console.log( 'UPR: ' + promise + ' with ' + error )
console.log( error.stack )
)
【讨论】:
catch() 应该用于处理错误,而不仅仅是报告错误。在最初的开发工作之后,我们通常想知道问题发生的时间和地点;上下文越多越好,所以使用宏或命名处理程序;.catch(fnErr)
打字并不费力...
将 unhandledRejection 用于 promise 并不是一个好主意,因为 unhandledRegjection 事件可以在其他地方触发。
节省错误处理时间和全局处理错误是一种不好的做法。但是unhandledRejection
可能是一种合理的方式来跟踪未被监督发现的未处理拒绝。但这就是问题所在。 error
实际上是拒绝,不一定是Error
的实例。虽然如果拒绝意味着错误,throw new Error
是一个好习惯,error
可以是任何东西并且错过stack
属性。所以我们在这里 - 粗心的开发人员没有及时处理的错误突然出现,没有调用堆栈(并且还会引发异常,因为 error
不是对象)。
@danavis,在这种情况下,堆栈为我提供了所需的所有上下文。事实上,这种技术是在开发过程中使用的,而不是实时应用程序(那会很懒惰和不幸)。
@estus 此代码旨在捕获开发过程中不需要特定错误处理的错误。您 100% 正确,这仅对未处理的拒绝有用,但生产代码永远不应该达到此目的。【参考方案2】:
对于开发过程中的错误跟踪,V8(最近的 Node.js 和 Chrome 版本)已经默认有 unhandledRejection
(Node.js) 和 unhandledrejection
(Chrome) 事件监听器,这会导致 UnhandledPromiseRejectionWarning
警告Chrome 中的 Node.js 和 Uncaught (in promise)
错误。
Node 7 中的弃用警告指出,这将在未来的 Node.js 版本中进行更改:
不推荐使用未处理的承诺拒绝。将来,未处理的 Promise 拒绝将使用非零退出代码终止 Node.js 进程。
promise 中的错误处理不是可选的,应与try...catch
同等对待。每个有可能被拒绝的承诺都应该用catch
链接。对于async...await
或co
,.catch
与try...catch
相同。
如果错误应该被忽略,则必须明确地捕获它。如果错误处理需要在整个应用程序中保持一致并符合日志级别,则可以通过设计来提供。例如。使用常规的debug
包:
const debug = require('debug');
function catchFn(err, ns = 'myapp:caughtPromises')
debug(ns)(err);
function catchPromise(promise, ns)
promise.catch(err => catchFn(err, ns));
return promise;
...
try
await promise;
catch (err)
catchFn(err);
// or
catchPromise(promise);
// or
promise.catch(catchFn);
catchFn
也可以扩展为使用第三方日志服务。
【讨论】:
这似乎不是 OP 所要求的。我认为 OP 有一个更高级别的.catch()
,所以拒绝没有得到处理。在我看来,他们在抱怨在不使用 Bluebird 之类的堆栈跟踪的情况下很难判断最初的拒绝在哪里。
@jfriend00 OP 用unhandledRejection
听众发布答案的事实不言而喻。 Promise 似乎没有更高级别的catch
,因为在这种情况下监听器不适用。如果问题首先是关于正确的堆栈跟踪,那么它的措辞不正确(在这种情况下,我建议坚持使用 BB 并且不要重新发明***,尤其是对于 Node)。
我使用node v7,确实没有默认处理程序。这就是我发布这个的原因。困扰我的是,没有 catch 的承诺确实显示错误,但没有行号和原因。至于“每个有机会被拒绝的承诺都应该与 catch 链接”:是的,如果确实没有默认处理程序。但在大型项目中,这意味着大量不必要的代码。
除非节点以--no-warnings
标志运行,否则Promise.reject();
将在节点6 和7 中产生UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): undefined
警告。我不确定是否存在其他可能抑制警告输出的条件。省略catch
。与省略 `try...catch 是一样的。这也是弃用消息所暗示的 - 它会在下一个 Node.js 中抛出异常。当然,您可以忽略良好做法并在全局范围内捕获所有异常,但在答案中建议这种方法是错误的。以上是关于防止 ES6 Promise 吞下错误(不使用 .catch)的主要内容,如果未能解决你的问题,请参考以下文章
IE浏览器对ES6不兼容的问题(语法错误Promise未定义Symbol未定义)