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

时间:2016-12-01 20:53:45

标签: javascript node.js es6-promise

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

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

导致此消息:;

(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.

我理解这背后的原因,因为许多程序员可能经历了Error最终被Promise吞噬的沮丧。然而,我做了这个实验:

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

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
},
0)

导致:

(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

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

但为什么会这样?

2 个答案:

答案 0 :(得分:20)

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

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

试试这个:

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

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

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
}, 0);

或者这个:

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

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
}, 0);

或者这个:

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

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
}, 0);

免责声明:我是caught模块的作者(是的,我是为这个答案写的)。

原理

added to Node之一是Breaking changes between v6 and v7。在Issue #830: Default Unhandled Rejection Detection Behavior中对此进行了激烈的讨论,并没有就异步附加的拒绝处理程序的承诺应该如何行事达成一致意见 - 在没有警告的情况下工作,处理警告或者通过终止程序来禁止使用它。在unhandled-rejections-spec项目的几个问题中进行了更多的讨论。

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

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

避免警告

如果分两步执行,附加空处理程序不会改变存储的promise的工作方式:

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

但这不一样:

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

此处prm2将是prm1的不同承诺。虽然prm1将因“失败”错误而被拒绝,但prm2将使用undefined来解决,这可能不是您想要的。

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

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

此处prm3prm1相同。

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

2017年更新

另请参阅Pull Request#6375:lib,src: "throw" on unhandled promise rejections(自2017年2月起尚未合并)标记为Milestone 8.0.0

  

承诺“抛出”拒绝像常规未捕获错误一样退出 [强调添加]

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

另请参阅Node.js 8.0.0 Tracking Issue #10117

答案 1 :(得分:1)

  

我认为异步处理Promise拒绝是一件坏事。

确实是的。

预计您希望立即处理任何拒绝。如果你做不到(并且可能无法处理它),你会收到警告 我几乎没有经历任何你不想在拒绝后立即失败的情况。即使你需要在失败后等待进一步的事情,你也应该明确地做到这一点。

我从未见过一个不可能立即安装错误处理程序的情况(试着说服我)。在您的情况下,如果您想要稍微延迟的错误消息,请执行

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

prm.catch((err) => {
    setTimeout(() => {
        console.log(err.message);
    }, 0);
});