我应该避免异步处理Promise拒绝吗?

rab*_*tco 15 javascript node.js es6-promise

我刚安装了Node v7.2.0并了解到以下代码:

var prm = Promise.reject(new Error('fail'));
Run Code Online (Sandbox Code Playgroud)

结果如下:

(node:4786) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: fail
(node:4786) 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.
Run Code Online (Sandbox Code Playgroud)

我理解这背后的原因,因为许多程序员可能已经经历了Error最终被一个人吞没的挫败感Promise.然而,我做了这个实验:

var prm = Promise.reject(new Error('fail'));

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
},
0)
Run Code Online (Sandbox Code Playgroud)

这导致:

(node:4860) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: fail
(node:4860) 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.
(node:4860) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
fail
Run Code Online (Sandbox Code Playgroud)

我基于PromiseRejectionHandledWarning假设异步处理Promise拒绝是/可能是一件坏事.

但那是为什么呢?

rsp*_*rsp 20

"我应该不要异步处理Promise拒绝吗?"

这些警告有一个重要的目的,但看看这一切是如何运作的,请看这些例子:

试试这个:

process.on('unhandledRejection', () => {});
process.on('rejectionHandled', () => {});

var prm = Promise.reject(new Error('fail'));

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
}, 0);
Run Code Online (Sandbox Code Playgroud)

或这个:

var prm = Promise.reject(new Error('fail'));
prm.catch(() => {});

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
}, 0);
Run Code Online (Sandbox Code Playgroud)

或这个:

var var caught = require('caught');
var prm = caught(Promise.reject(new Error('fail')));

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
}, 0);
Run Code Online (Sandbox Code Playgroud)

免责声明:我是被捕获模块的作者(是的,我为此答案写了它).

合理

它被添加到Node作为v6和v7之间Breaking更改之一.在问题#830中对此进行了热烈的讨论:默认的未处理拒绝检测行为,没有就异步附加的拒绝处理程序的承诺如何表现的普遍协议 - 没有警告工作,处理警告或通过终止程序禁止使用.在未处理 - 拒绝 - 规范项目的几个问题中进行了更多的讨论.

此警告是为了帮助您找到忘记处理拒绝的情况,但有时您可能希望避免这种情况.例如,您可能想要生成一堆请求并将生成的promises存储在一个数组中,以便稍后在程序的其他部分处理它.

承诺优于回调的一个优点是,您可以将创建承诺的位置与附加处理程序的位置(或位置)分开.这些警告使得操作变得更加困难,但您可以处理事件(我的第一个示例)或者在您创建不希望立即处理的承诺的任何地方附加虚拟捕获处理程序(第二个示例).或者你可以让一个模块为你做(第三个例子).

避免警告

如果您分两步执行此操作,则附加空处理程序不会更改存储的promise以任何方式工作的方式:

var prm1 = Promise.reject(new Error('fail'));
prm1.catch(() => {});
Run Code Online (Sandbox Code Playgroud)

但这不会是相同的:

var prm2 = Promise.reject(new Error('fail')).catch(() => {});
Run Code Online (Sandbox Code Playgroud)

prm2将是一个不同的承诺prm1.虽然prm1会因"失败"错误而被拒绝,但prm2会解决undefined哪个可能不是你想要的.

但是你可以编写一个简单的函数来让它像上面的两步示例一样工作,就像我对caught模块所做的那样:

var prm3 = caught(Promise.reject(new Error('fail')));
Run Code Online (Sandbox Code Playgroud)

prm3是一样的prm1.

请参阅:https://www.npmjs.com/package/caught

2017年更新

另请参阅Pull Request#6375:lib,src:"抛出"未标记的承诺拒绝(尚未合并,截至2017年2月),标记为Milestone 8.0.0:

使Promises"抛出"拒绝,退出像常规未被捕获的错误.[强调补充]

这意味着我们可以期望Node 8.x将此问题所引发的警告更改为崩溃并终止该过程的错误,我们应该在今天编写程序时将其考虑在内以避免将来出现意外.

另请参见Node.js 8.0.0跟踪问题#10117.

  • 为了确保完全准确,我认为重要的是要弄清将要发生的确切更改(不早于Node 10,因为9已经没有交付)。如果Promise被垃圾回收,它将仅以非零代码退出。因此,只有在您没有提及Promise的情况下,才不可能被抓住。异步捕获的Promise绝不会导致进程退出,因为这会破坏许多标准模式。这些警告仍然会出现,因为无法知道是否会被抓住。 (2认同)