防止 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 这样的整个库让我感到不舒服。

【问题讨论】:

“你使用的每一个承诺”是什么意思?你只需要在链尾catcherrors。另外,“吞”是什么意思?如果你想防止这种情况发生,错误应该怎么办? 当使用大量的 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...awaitco.catchtry...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)的主要内容,如果未能解决你的问题,请参考以下文章

重读es6, 正确了解promise中catch的用法

ES6 promise时序,本质,常见错误

IE浏览器对ES6不兼容的问题(语法错误Promise未定义Symbol未定义)

ES6深入浅出-9 Promise-3.Promise的细节

ES6 很可能会出现简单的 promise 错误

如何防止 Sublime Text 2 吞下右括号、引号和圆括号?