NodeJS中的异步编程经验

问题引入:今天在 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: "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

#Promise


你可能感兴趣的:(NodeJS中的异步编程经验)