主题
Promise 与事件循环
Promise 不只是“更好写的异步语法”,它和事件循环直接相关。真正理解 Promise.then()、await 的执行时机,需要把它放回事件循环模型里看。
先说结论
Promise的回调不会同步立即执行。then/catch/finally的回调会进入微任务队列。- 当前同步代码执行完之后,浏览器会先清空微任务,再进入下一轮任务调度。
一个最基础的例子
js
console.log(1)
Promise.resolve().then(() => {
console.log(2)
})
console.log(3)输出顺序是:
js
1
3
2原因是:
console.log(1)同步执行。Promise.then回调进入微任务队列。console.log(3)同步执行。- 同步代码结束后,清空微任务,输出
2。
为什么是微任务
浏览器事件循环中,常见可以分为两类理解:
- 任务:例如
setTimeout、DOM 事件、脚本执行 - 微任务:例如
Promise.then、queueMicrotask、MutationObserver
微任务优先级更高。每轮任务执行结束后,都会先把当前产生的微任务清空。
对比 setTimeout
js
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
Promise.resolve().then(() => {
console.log(3)
})
console.log(4)输出顺序:
js
1
4
3
2原因:
setTimeout回调进入后续任务队列Promise.then回调进入微任务队列- 当前同步代码结束后,先执行微任务,再等下一轮任务执行
setTimeout
Promise 链为什么是按顺序执行
js
Promise.resolve()
.then(() => {
console.log(1)
})
.then(() => {
console.log(2)
})第一个 then 执行完后,第二个 then 对应的回调会继续进入微任务队列,因此看起来就像“连续异步但顺序稳定”。
await 为什么也像微任务
async/await 本质上是 Promise 的语法糖,所以 await 后续代码的恢复执行,也会走微任务调度。
js
async function run() {
console.log(1)
await Promise.resolve()
console.log(2)
}
console.log(0)
run()
console.log(3)输出顺序:
js
0
1
3
2一个常见面试题
js
console.log('start')
setTimeout(() => {
console.log('timeout')
}, 0)
Promise.resolve()
.then(() => {
console.log('promise1')
})
.then(() => {
console.log('promise2')
})
console.log('end')输出:
js
start
end
promise1
promise2
timeout理解顺序
遇到这类题目,可以按这个顺序看:
- 先执行所有同步代码。
- 把
Promise.then/await后续逻辑放进微任务。 - 把
setTimeout之类放进后续任务。 - 同步结束后先清空微任务。
- 再进入下一轮任务执行。
和浏览器事件循环页的关系
/web/core/web-api/advance/event-loop更偏浏览器运行机制- 当前这页更偏 JavaScript 语言层如何借助微任务调度
Promise
两页合起来看,会更容易把异步执行顺序真正串起来。
