This website requires JavaScript.
ROSY
GRAY

觉宇宙之无穷,识盈虚之有数。

——王勃/《滕王阁序》

BING
转载

关于 Unhandled Rejection

共 5,275 字,需阅读 13 分钟2021/02/22 上午67 次阅读

问题引入:今天在 Gulp 构建任务中出现一个 html 解析错误,但是并没有报错,也没有中断 gulp 构建任务的执行,而是出现 UnhandledPromiseRejectionWarning 的警告,所以会误以为构建成功,这篇文章将对此进行探究并解决该问题。

              
  • 1
  • 2
  • 3
  • 4
  • 5
(node:24866) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error in plugin 'gulp-posthtml' Message: Parse Error: <img id="titleIcon" class$="{{getStypeType_(info.stype)}}" src$="{{getTitleIcon_(in ... (node:24866) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

关于 Unhandled Rejection

一个 Promise 是一个异步操作的状态机,其可能处于这三种状态之一

  • pending:异步操作还在执行中
  • fulfilled:异步操作已经完成
  • rejected:异步操作执行失败

Node.js 6.6.0 added a sporadically useful bug/feature: logging unhandled promise rejections to the console by default.

在 Node.js 6.6.0 中增加了一个特性:对 Promise 中未处理的 rejection 默认会输出 UnhandledPromiseRejectionWarning 提示

例如:test.js 中有如下代码:

              
  • 1
  • 2
  • 3
new Promise((resolve, reject) => { setTimeout(() => reject('woops'), 500); });

node test.js 执行:

              
  • 1
  • 2
(node:47122) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): error (node:47122) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code

另一种情况是直接在 Promise 中抛出异常:

              
  • 1
new Promise(() => { throw new Error('exception!'); });

执行后也会有 UnhandledPromiseRejectionWarning 的警告:

              
  • 1
  • 2
(node:47657) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: exception! (node:47657) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Promise API 中有 .catch() 这个方法,可以用来处理捕捉 rejection 进行处理

              
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
new Promise((resolve, reject) => { setTimeout(() => reject('error'), 500); }) .catch(error => console.log('caught', error)) new Promise(() => { throw new Error('exception!'); }) .catch(error => console.log('caught', error.message))

但是注意:

              
  • 1
  • 2
new Promise((_, reject) => reject(new Error('woops'))) .catch(error => { console.log('caught', err.message); });

这个例子中虽然用 .catch() 捕捉处理了 Promise 中的 rejection;但是注意在 err.message 中的 err 是未定义的,代码执行时会抛出错误,由于没有后续的处理,所以也会输出 UnhandledPromiseRejectionWarning 的警告

              
  • 1
  • 2
(node:47918) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): ReferenceError: err is not defined (node:47918) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

所以稍不注意就会引起 Promise 中的 unhandled rejections 😨

unhandledRejection 事件

在 node process 中有一个 unhandledRejection 事件,当没有对 Promise 的 rejection 进行处理就会抛出这个事件(这只对原生 Promise 有效)

The unhandledrejection event is fired when a JavaScript Promise is rejected but there is no rejection handler to deal with the rejection.

              
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
process.on('unhandledRejection', error => { // Will print "unhandledRejection err is not defined" console.log('unhandledRejection', error.message); }); new Promise((_, reject) => reject(new Error('woops'))) .catch(error => { console.log('caught', err.message); });

此时执行后,就没有 UnhandledPromiseRejectionWarning 的警告输出了,只输出:unhandledRejection err is not defined

如果我们不想监听 unhandledRejection 事件,也不想看到 UnhandledPromiseRejectionWarning 的警告输出,怎么办呢?

              
  • 1
  • 2
new Promise((_, reject) => reject(new Error('woops'))) .catch(new Function());

我们可以在 .catch() 中传入一个空函数,假装对 rejection 进行了处理,这样也没有触发 unhandledRejection 事件

Async/Await

关于 Async/Await,可以参考文章: ES7 中的 async await ,在这篇文章中详细介绍了 Async/Await 并且和 Promise 进行了对比,Async/Await 在处理异步操作上的优势更明显。

              
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
async function test() { // No unhandled rejection! await Promise.reject(new Error('test')); } test(); // 输出: // (node:54358) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3): Error: test // (node:54358) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. test().catch(error => console.log(error.message)); // 输出: // test

async 异步函数返回的是 Promise,所以执行异步函数后,统一需要用 .catch() 对可能出现的 rejection 进行捕捉处理,否则统一也是会出现 UnhandledPromiseRejectionWarning 的警告

解决问题

最后解决一下文章开头的问题:构建任务中 html 解析错误,出现了一个 Unhandled Rejection,所以我们可以添加一个 unhandledRejection 事件监听,直接退出:

              
  • 1
  • 2
  • 3
  • 4
process.on('unhandledRejection', error => { console.error('unhandledRejection', error); process.exit(1) // To exit with a 'failure' code });

参考链接

Unhandled Promise Rejections in Node.js

原文链接

Node.js 中的 UnhandledPromiseRejectionWarning 问题

本文于2021/02/22 上午发布在FAQ
自由转载 - 署名 - 非商业性使用https://blog.rosygray.com/article/19
0 / 0 条看法
访客身份
在下有一拙见,不知...
期待你的捷足先登