Promise未触发unhandledrejection的时长及浏览器检查时机疑问
浏览器何时检查未处理的Promise?
我原本以为浏览器会在事件循环tick末尾检查未处理的Promise,但实际实验结果和预期不符。
首先注册两个事件监听器:
window.addEventListener('unhandledrejection', function(event) { console.log('UNHANDLED!'); event.preventDefault(); }); window.addEventListener('rejectionhandled', function(event) { console.log('HANDLED!'); event.preventDefault(); });
实验1:0延迟的setTimeout
运行以下代码:
let promise = Promise.reject(new Error("Promise Failed!")); // 计划在下一个tick处理,按我的理解应该在unhandledrejection触发之后执行 setTimeout(() => promise.catch(err => console.log('CAUGHT!')), 0);
预期会触发unhandledrejection和rejectionhandled事件,但实际控制台没有输出对应的UNHANDLED!和HANDLED!消息。
实验2:1ms延迟的setTimeout
修改延迟时间后:
let promise = Promise.reject(new Error("Promise Failed!")); // 计划1ms后处理,确实在unhandledrejection触发之后执行 setTimeout(() => promise.catch(err => console.log('CAUGHT!')), 1);
此时unhandledrejection和rejectionhandled事件正常触发。
原因解释
这是因为浏览器对未处理Promise的检查时机,并非简单的“事件循环tick末尾”,而是采用了微任务队列清空后的延迟检查机制:
- 当Promise被reject且无错误处理器时,浏览器不会立刻触发
unhandledrejection,而是会预留一个极短的“窗口期”,观察是否有代码为该Promise添加错误处理器(如.catch())。 setTimeout(fn, 0)的回调会被安排在下一个事件循环宏任务队列的开头,此时浏览器的延迟检查尚未触发——微任务队列清空后,浏览器优先执行了这个0延迟的回调,回调中给Promise加上了.catch(),错误被提前处理,因此不会触发unhandledrejection。- 而
setTimeout(fn, 1)的回调会被推迟到至少1ms后执行,这段时间足够浏览器完成未处理Promise的延迟检查,触发unhandledrejection;之后回调执行添加.catch(),又会触发rejectionhandled。
简单来说,浏览器给了开发者一个缓冲时间:在微任务执行完毕后,不会立刻判定Promise错误未处理,而是等当前宏任务相关的后续操作完成且无新处理器添加时,才会触发unhandledrejection。0延迟的setTimeout刚好卡在这个缓冲期内,提前处理了错误,所以事件不会触发。
内容的提问来源于stack exchange,提问作者Ilia Yatsenko
相关产品推荐
相关产品推荐

