题目
浏览器事件循环机制解析与宏任务/微任务执行顺序
信息
- 类型:问答
- 难度:⭐⭐
考点
事件循环原理,宏任务与微任务区别,异步代码执行顺序
快速回答
事件循环是浏览器处理异步任务的核心机制,其执行顺序为:
- 执行同步代码(调用栈清空)
- 执行所有微任务(如Promise)
- 执行一个宏任务(如setTimeout)
- 重复步骤2-3
关键区别:
- 宏任务:setTimeout, setInterval, I/O, UI渲染
- 微任务:Promise.then, MutationObserver, queueMicrotask
1. 事件循环原理
浏览器采用单线程执行JavaScript,通过事件循环处理异步操作。核心组件:
- 调用栈(Call Stack):同步代码执行栈
- 任务队列(Task Queue):宏任务队列
- 微任务队列(Microtask Queue):优先级更高的异步队列
- 事件循环线程:持续检查调用栈和队列
2. 执行顺序示例
console.log('1'); // 同步
setTimeout(() => console.log('2'), 0); // 宏任务
Promise.resolve().then(() => {
console.log('3'); // 微任务
setTimeout(() => console.log('4'), 0); // 嵌套宏任务
});
console.log('5'); // 同步
// 输出顺序:1 → 5 → 3 → 2 → 43. 执行流程详解
- 同步代码执行:输出1和5
- 清空微任务队列:Promise.then回调输出3,同时注册新宏任务
- 执行一个宏任务:第一个setTimeout输出2
- 再次清空微任务队列(此时为空)
- 执行下一个宏任务:嵌套setTimeout输出4
4. 宏任务与微任务对比
| 特性 | 宏任务(macrotask) | 微任务(microtask) |
|---|---|---|
| 常见API | setTimeout, setInterval, I/O, UI渲染 | Promise.then/catch/finally, MutationObserver, queueMicrotask |
| 队列机制 | 多个独立队列(定时器/I/O等) | 单个高优先级队列 |
| 执行时机 | 每次循环取一个任务 | 每轮循环全部清空 |
5. 最佳实践
- 耗时操作用宏任务避免阻塞渲染
- 关联异步操作使用Promise链保证执行顺序
- 避免微任务嵌套过深导致饥饿(如递归添加微任务)
- UI更新优先使用requestAnimationFrame
6. 常见错误
- 误以为setTimeout(0)会立即执行(实际有最小延迟4ms)
- 在微任务中同步修改DOM导致布局抖动
- 忽略async/await本质是微任务包装
- 宏任务中错误处理未用Promise.catch捕获
7. 扩展知识
- requestAnimationFrame:在渲染前执行,适合动画
- Web Workers:解决CPU密集型任务阻塞主线程
- Node.js事件循环:额外包含process.nextTick和setImmediate
- 性能影响:微任务过多会延迟渲染(如无限Promise递归)