JavaScript事件循环按“同步代码→微任务→渲染→宏任务”严格分层执行;宏任务如setTimeout、click事件独立运行不中断,微任务如Promise.then在宏任务后立即清空执行,决定代码输出顺序与性能表现。

JavaScript 事件循环的核心是“一次只执行一个宏任务,之后立刻清空所有微任务”。它不是并行调度,而是严格按优先级分层推进:同步代码 → 微任务 → 渲染 → 下一个宏任务。
宏任务是事件循环的主干节点
每个宏任务代表一个独立的执行单元,会完整运行到底,中途不会被中断。常见宏任务包括:
- 整个 script 标签(初始执行环境)
- setTimeout 和 setInterval 的回调
- 用户交互事件(如 click、input)
- I/O 回调(如 fetch 完成、fs.readFile)
- 浏览器中的 UI 渲染(本身也属于宏任务阶段的一部分)
微任务是宏任务结束后的“即时响应层”
微任务在当前宏任务执行完毕、渲染前立即执行,且必须全部执行完才进入下一宏任务。它们由 JS 引擎直接管理,响应更快。典型微任务有:
- Promise.then/catch/finally 回调
- queueMicrotask() 显式注册的微任务
- MutationObserver 的回调(监听 DOM 变化)
- Node.js 中的 process.nextTick()(优先级比 Promise 还高)
执行顺序决定行为差异
关键不在“谁先注册”,而在“谁先被调度”。例如:
立即学习“Java免费学习笔记(深入)”;
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
输出一定是 1 → 4 → 3 → 2。因为:
- 1 和 4 是同步代码,立即执行
- setTimeout 注册为宏任务,排在下一轮事件循环
- Promise.then 注册为微任务,在本轮宏任务(脚本)结束后立刻执行
为什么这个区别很重要
微任务适合需要“尽快但不阻塞”的逻辑,比如状态更新后立即触发视图同步(Vue/React 的 nextTick)、链式异步处理;宏任务更适合延迟、分片或与 UI 渲染配合的操作,比如用 setTimeout(fn, 0) 让出主线程、避免长任务卡顿。错把高频更新塞进微任务,可能引发队列堆积;该用微任务却用了 setTimeout,则会多经历一次渲染延迟,影响响应感。










