【发布时间】:2020-12-21 18:18:12
【问题描述】:
在 node.js 中,有没有很好地解释异常如何与承诺的“拒绝”一起工作?
【问题讨论】:
-
“重复”并不能很好地解释异常如何与拒绝承诺一起工作。它被标记为意见。
在 node.js 中,有没有很好地解释异常如何与承诺的“拒绝”一起工作?
【问题讨论】:
承诺拒绝是同步代码中异常的异步模拟。在 Promise 处理程序中抛出的异常(在 then()、catch() 或 finally() 中)会自动转换为 Promise 拒绝。
然而,事实并非如此。如果您创建一个承诺,或调用一个返回最终将被拒绝的承诺的函数,则在 try/catch 块内不会自动转换为异常。但是您可以使用async/await 来完成。这将起作用:
async function() {
try {
await getPromiseThatWillBeRejected();
}
catch (e) {
// error that caused rejection can be handled here
}
}
await 关键字实际上将其转换为同步阻塞调用(它并不是真正的阻塞,但后续代码仅在 promise 完成或拒绝后执行)并将拒绝转换为同步异常。
【讨论】:
以下是异常和承诺拒绝组合的演练。
让我们从一个抛出异常的 Promise 和另一个以拒绝完成 Promise 的 Promise 开始,对它们进行比较。
function thrower() {
return new Promise((resolve, reject) => { throw new Error('thrown'); });
}
function rejecter() {
return new Promise((resolve, reject) => reject('rejected'));
}
下面的调用代码同样会收到错误:
thrower.catch(ex => console.log('caught ' + ex));
rejecter.catch(ex => console.log('caught ' + ex));
结果是caught Error: thrown 和caught rejected。到达catch 函数的异常是直截了当的。但奇怪的是,被拒绝的 Promise 也会出现在 catch 函数中。就是这样,让我们继续。
同样,异常变成拒绝。下面使用then的第二个参数提供onrejection函数:
thrower().then(() => {}, err => console.log('err ' + err));
rejecter().then(() => {}, err => console.log('err ' + err));
结果是err Error: thrown 和err rejected。同样,这是有道理的,拒绝到达onrejection 处理程序。这是一个延伸,但我可以肯定,例外也在那里。
现在如果同时使用onrejection 处理程序和catch 会怎样?
thrower().then(() => {}, err => console.log('err ' + err)).catch(ex => console.log('caught ' + ex));
rejecter().then(() => {}, err => console.log('err ' + err)).catch(ex => console.log('caught ' + ex));
结果是err Error: thrown 和err rejected。这里有些混乱。该语言将异常路由到 err 函数,而不是 catch 函数。
重要的一点,这段代码没有捕捉到异常:
try {
thrower();
}
catch(ex) {
console.log('caught ' + ex); // won't catch thrower's exception
}
为什么 - try/catch 块执行在承诺执行之前完成。
但这也不太明显:
function throwerThrower() {
return new Promise((resolve, reject) => { thrower().catch(ex => { throw ex }); });
}
throwerThrower().catch(ex => console.log('caught ' + ex)); // not going to catch nested throw
像上面那样级联异常会导致未处理的异常。
但是下一个块确实有效,并且可能显示拒绝而不是抛出的价值:
function throwerRejecter() {
return new Promise((resolve, reject) => { thrower().catch(ex => reject(ex)); });
}
throwerRejecter().catch(ex => console.log('caught ' + ex));
最后,这是最后两个嵌套组合:
function rejecterThrower() {
return new Promise((resolve, reject) => { rejecter().catch(ex => { throw ex }); });
}
rejecterThrower().catch(ex => console.log('caught ' + ex));
返回ERR_UNHANDLED_REJECTION,这看起来很合理,因为拒绝处理引发了异常。
和
function rejecterRejecter() {
return new Promise((resolve, reject) => { rejecter().catch(ex => reject(ex)); });
}
rejecterRejecter().catch(ex => console.log('caught ' + ex));
打印caught rejected - 很明显的拒绝的简单级联。
【讨论】:
then 的第二个参数)与传递给catch() 的参数基本相同,这并不令人困惑。在遇到catch() 之前已经捕获了异常/拒绝。