JavaScript 事件循环到底怎么运行
理解事件循环,可以解释为什么代码书写顺序和实际输出顺序有时并不一致。
先用白话理解
这篇内容在讲什么?
事件循环决定 JavaScript 什么时候执行同步代码、Promise 和定时器。
问题
为什么 Promise 经常比写在前面的 setTimeout 更早输出?
同步代码结束后会先清空微任务,再处理定时器等宏任务。
解决方案
- 先标记同步语句
- 再找 Promise 微任务
- 最后处理 setTimeout
- 用控制台验证
完整执行顺序实验示例代码
console.log('1:同步开始')
setTimeout(() => {
console.log('5:定时器宏任务')
}, 0)
Promise.resolve()
.then(() => {
console.log('3:第一个微任务')
return Promise.resolve()
})
.then(() => {
console.log('4:第二个微任务')
})
console.log('2:同步结束')
// 输出顺序:1 → 2 → 3 → 4 → 5先执行同步任务
JavaScript 会先完成当前调用栈中的同步代码,只有调用栈清空后,才会处理等待队列中的任务。
微任务通常更早执行
Promise 回调属于微任务,当前同步代码结束后会优先清空微任务,再进入 setTimeout 等宏任务。
- 同步代码
- Promise 微任务
- 定时器宏任务
不要长时间占用主线程
大量计算会阻塞渲染与点击响应,可拆分任务、使用 Web Worker 或交给服务端处理。
结语
判断最终要回到真实体验
技术方案没有脱离场景的标准答案。把用户任务、业务边界、开发成本和长期维护放在一起评估,才有可能做出稳定、清晰且经得起变化的页面。
照着做的步骤
- 先找同步代码
- 再找 Promise 微任务
- 最后看定时器等宏任务
- 用控制台验证顺序
容易踩的坑
- 认为 setTimeout 0 会立即执行
- 在主线程做大量计算
- 混淆代码顺序和执行顺序
术语不用怕
- 调用栈:当前正在执行的函数
- 微任务:Promise 等高优先级任务
- 宏任务:定时器和事件等任务