Promise执行顺序疑问:setTimeout延迟为何影响输出结果
为什么setTimeout延迟时间会影响unhandledrejection事件的触发结果?
先看问题中的代码:
let num = 10; window.addEventListener("unhandledrejection", (event) => { num = 20 }); let promise = new Promise((resolve, reject) => { reject("reject"); }); setTimeout(() => { promise.catch(() => { console.log(num); }); }, 1);
现象差异
- 当
setTimeout延迟时间小于1ms时,输出10,num始终未被修改,unhandledrejection事件未触发。 - 当
setTimeout延迟时间大于等于1ms时,输出20,num被事件回调修改。
分步执行顺序解析
核心在于浏览器对Promise未捕获拒绝的检测时机,以及宏任务队列的执行优先级:
情况1:setTimeout延迟<1ms
- 同步代码阶段:
- 声明
num=10,绑定unhandledrejection事件回调。 - 创建Promise并立即调用
reject,Promise状态变为rejected,此时没有任何catch回调绑定。 - 调用
setTimeout,由于延迟小于1ms,浏览器会把该回调插入到宏任务队列的最前端(优先执行)。
- 声明
- 同步代码结束后:
- 微任务队列为空(没有待执行的微任务),直接进入下一个宏任务执行。
- 执行setTimeout回调:
- 给Promise绑定
catch回调,此时Promise的拒绝被“捕获”,浏览器会取消后续的unhandledrejection事件触发逻辑。 - 执行
catch回调,打印当前的num=10。
- 给Promise绑定
- 后续浏览器再检测Promise状态时,发现已有
catch处理,unhandledrejection事件永远不会触发,num保持10。
情况2:setTimeout延迟>=1ms
- 同步代码阶段:和情况1完全一致,Promise被
reject且无catch回调,setTimeout回调被加入宏任务队列,但因延迟足够长,排在队列靠后位置。 - 同步代码结束后:
- 微任务队列为空,浏览器开始检测Promise状态:发现
rejected状态的Promise没有任何catch处理,于是将unhandledrejection事件回调加入宏任务队列(优先级高于延迟>=1ms的setTimeout回调)。
- 微任务队列为空,浏览器开始检测Promise状态:发现
- 执行unhandledrejection事件回调:
- 修改
num为20。
- 修改
- 执行setTimeout回调:
- 给Promise绑定
catch回调(此时Promise的拒绝已被标记为“未捕获”,但catch仍能正常处理)。 - 执行
catch回调,打印当前的num=20。
- 给Promise绑定
内容的提问来源于stack exchange,提问作者byles1506
相关产品推荐
相关产品推荐

