You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

  1. 同步代码阶段:
    • 声明num=10,绑定unhandledrejection事件回调。
    • 创建Promise并立即调用reject,Promise状态变为rejected,此时没有任何catch回调绑定。
    • 调用setTimeout,由于延迟小于1ms,浏览器会把该回调插入到宏任务队列的最前端(优先执行)。
  2. 同步代码结束后:
    • 微任务队列为空(没有待执行的微任务),直接进入下一个宏任务执行。
  3. 执行setTimeout回调:
    • 给Promise绑定catch回调,此时Promise的拒绝被“捕获”,浏览器会取消后续的unhandledrejection事件触发逻辑。
    • 执行catch回调,打印当前的num=10。
  4. 后续浏览器再检测Promise状态时,发现已有catch处理,unhandledrejection事件永远不会触发,num保持10。

情况2:setTimeout延迟>=1ms

  1. 同步代码阶段:和情况1完全一致,Promise被reject且无catch回调,setTimeout回调被加入宏任务队列,但因延迟足够长,排在队列靠后位置。
  2. 同步代码结束后:
    • 微任务队列为空,浏览器开始检测Promise状态:发现rejected状态的Promise没有任何catch处理,于是将unhandledrejection事件回调加入宏任务队列(优先级高于延迟>=1ms的setTimeout回调)。
  3. 执行unhandledrejection事件回调:
    • 修改num为20。
  4. 执行setTimeout回调:
    • 给Promise绑定catch回调(此时Promise的拒绝已被标记为“未捕获”,但catch仍能正常处理)。
    • 执行catch回调,打印当前的num=20。

内容的提问来源于stack exchange,提问作者byles1506

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 13:57:36