注意:有关 Node v15 中的更改,请参阅下面的 2020 更新
“我应该避免异步处理 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 caught = require('caught');
var prm = caught(Promise.reject(new Error('fail')));
setTimeout(() => {
prm.catch((err) => {
console.log(err.message);
})
}, 0);
免责声明:我是被捕获module的作者(是的,我是为这个答案写的)。
基本原理
它被添加到 Node作为v6 和 v7 之间的重大更改之一。在问题 #830: Default Unhandled Rejection Detection Behavior 中对它进行了激烈的讨论,但对于异步附加拒绝处理程序的Promise应该如何表现没有普遍的共识 - 没有警告地工作,有警告地工作或通过终止程序被禁止使用. 在unhandled-rejections-spec项目的几个问题中进行了更多的讨论。
此警告是为了帮助您找到忘记处理拒绝的情况,但有时您可能想避免它。例如,您可能希望发出一堆请求并将结果 promise 存储在一个数组中,以便稍后在程序的其他部分进行处理。
Promise相对于回调的优势之一是,您可以将创建Promise的位置与附加处理程序的位置(或多个位置)分开。这些警告使操作变得更加困难,但是您可以处理事件(我的第一个示例)或在创建不想立即处理的Promise的任何位置附加一个虚拟捕获处理程序(第二个示例)。或者你可以让一个module为你做这件事(第三个例子)。
避免警告
如果您分两步执行,附加空处理程序不会以任何方式改变存储的Promise的工作方式:
var prm1 = Promise.reject(new Error('fail'));
prm1.catch(() => {});
但是,这将不一样:
var prm2 = Promise.reject(new Error('fail')).catch(() => {});
这prm2
将是一个不同的Promiseprm1
。虽然prm1
会因“失败”错误而被拒绝,但prm2
将得到解决,undefined
这可能不是您想要的。
但是您可以编写一个简单的函数来使其像上面的两步示例一样工作,就像我对caught
module所做的那样:
var prm3 = caught(Promise.reject(new Error('fail')));
这里prm3
与prm1
.
见:https : //www.npmjs.com/package/caught
2017年更新
另请参见 Pull Request #6375: lib,src: "throw" on unhandled promise denied (截至 2017 年二月尚未合并),标记为Milestone 8.0.0:
使 Promises“抛出”拒绝,这些拒绝像常规未捕获的错误一样退出。【强调】
这意味着我们可以期望 Node 8.x 将有关此问题的警告更改为崩溃并终止进程的错误,我们在今天编写程序时应该考虑到这一点,以避免将来出现意外。
另请参阅Node.js 8.0.0 跟踪问题 #10117。
2020 更新
另请参阅 Pull Request #33021:process: Change default --unhandled-rejections=throw(已经合并并作为 v15 版本的一部分发布 - 请参阅:发行说明)再次使其成为例外:
从 Node.js 15 开始, 的默认模式unhandledRejection
更改为throw
(from warn
)。在throw
模式下,如果unhandledRejection
未设置挂钩,则将unhandledRejection
作为未捕获的异常引发。拥有unhandledRejection
钩子的用户应该不会看到行为的变化,并且仍然可以使用--unhandled-rejections=mode
进程标志来切换模式。
这意味着 Node 15.x 终于将这个问题即将发生的警告更改为错误,所以正如我在上面 2017 年所说的,我们在编写程序时绝对应该考虑到它,因为如果我们不这样做,它肯定会导致将运行时升级到 Node 15.x 或更高版本时出现问题。