当使用 async await 时,有时我们希望将 Promise 保存在一个变量中,做一些其他的事情,然后再等待它。如果“其他东西”不需要很长时间,这可以正常工作:
try {
const pWillReject = rejectAfter(8000);
const someValue = await shortRunning();
doSomethingWith(someValue);
await pWillReject;
} catch (ex) {
// handle the error
console.log('caught', ex);
}
// continue
错误按预期被catch捕获,代码执行可以继续
但是,如果“其他事情”花费的时间太长,我们可能会收到“未处理的承诺拒绝”通知。代码执行被中断,catch 没有被执行。发生这种情况是因为,尽管我们最终附加了一个处理程序,但直到错误事件已经触发之后才完成。
try {
const pWillReject = rejectAfter(8000);
const someValue = await longRunning();
doSomethingWith(someValue);
// we never get here
await pWillReject;
} catch (ex) {
//catch is not executed
console.log('caught', ex);
}
// no oppurtunity to continue
我不想为 UnhandledPromiseRejection 添加全局处理程序,因为在大多数情况下,我希望程序在这种情况下终止(因为它表示编码错误)。但是在这种情况下,在处理拒绝的情况下(只是不够快),程序应该继续。
这个问题有什么好的解决方法?
我能想到的最好的方法是以非侵入性方式附加一个无操作处理程序,即
const pWillReject = rejectAfter(8000);
pWillReject.catch(ex=>{}); // do nothing, errors will be handled later
但我不喜欢这个
- 它使代码嘈杂
- 它不是自我记录 - 需要评论
- 最重要的是 - 它可能会错过真正的编码错误。如果承诺从未被等待,它可能会默默地失败。
有更好的选择吗?
完整代码示例:
async function go() {
try {
const pWillReject = rejectAfter(8000);
const someValue = await shortRunning();
doSomethingWith(someValue);
await pWillReject;
} catch (ex) {
console.log('caught', ex);
}
console.log('success');
try {
const pWillReject = rejectAfter(8000);
const someValue = await longRunning();
doSomethingWith(someValue);
await pWillReject;
} catch (ex) {
console.log('caught', ex);
}
console.log('failure');
}
go().catch(ex=> {
console.log('global catch');
})
async function delay(ms) {
return new Promise((r,x)=> {
setTimeout(r, ms);
});
}
async function rejectAfter(ms) {
return new Promise((r,x)=> {
setTimeout(x, ms, new Error("whatever"));
});
}
function shortRunning() {
return delay(1000);
}
function longRunning() {
return delay(10000);
}
function doSomethingWith(obj) {
console.info(obj);
}
注意在我的情况下,我使用的是节点,但是我认为这个问题同样适用于浏览器。