Promise.then回调属于微任务,setTimeout回调属于宏任务,因此Promise.then总在setTimeout之前执行;同步代码执行完后立即清空微任务队列,再取下一个宏任务。

Promise.then 的回调属于微任务,setTimeout 的回调属于宏任务,所以 Promise.then 总是先于 setTimeout 执行,哪怕 setTimeout 的延迟设为 0。
执行顺序由事件循环阶段决定
JavaScript 的事件循环中,每次执行完一个宏任务(如 script 脚本、setTimeout 回调)后,会清空当前所有微任务队列,再取下一个宏任务。Promise.then 注册的回调会被放入微任务队列,而 setTimeout 放入的是宏任务队列。
- 同步代码立即执行
- 遇到 Promise.then,把回调推入微任务队列(暂不执行)
- 遇到 setTimeout,把回调推入宏任务队列(也暂不执行)
- 当前宏任务(比如整个脚本)执行完,立刻执行所有排队的微任务(包括所有已 resolve 的 Promise.then)
- 微任务队列清空后,才从宏任务队列取下一个(比如 setTimeout 回调)执行
典型例子看输出顺序
下面这段代码的打印顺序是:1 → 2 → 4 → 3
console.log(1); Promise.resolve().then(() => console.log(2)); setTimeout(() => console.log(3), 0); console.log(4);
- 1 和 4 是同步输出,按顺序立刻打印
- then 回调是微任务,在同步代码结束后、setTimeout 前执行 → 输出 2
- setTimeout 回调是宏任务,排在下一轮事件循环 → 输出 3
多个 Promise.then 和 setTimeout 混用的情况
微任务之间按注册顺序执行,宏任务之间也按触发顺序排队,但所有微任务始终优先于后续宏任务。
- 连续两个 then:第一个 then 的回调执行完,若它返回新 Promise,第二个 then 会等这个新 Promise resolve 后再进微任务队列(仍属于本轮微任务清空阶段)
- 多个 setTimeout:即使 delay 都是 0,它们按调用顺序进入宏任务队列,依次执行
- 但只要有一个 Promise.then,它的回调就一定比同轮中任意 setTimeout 先执行
注意:resolve 的时机影响 then 执行时机
Promise.then 的回调只在 Promise 状态变为 fulfilled 后才被加入微任务队列。如果 Promise 是异步 resolve(比如包装了 setTimeout),那么 then 回调要等到那个异步操作完成后再进微任务队列。
-
Promise.resolve().then(...):立即进微任务队列 -
new Promise(r => setTimeout(r, 0)).then(...):先等 setTimeout 宏任务执行 r(),再把 then 加入微任务队列 → 实际执行晚于同级的 setTimeout(0)


















