javascript event loop 执行顺序为:先执行同步代码,再清空微任务队列,最后执行下一个宏任务;如例题输出为 script start → script end → promise1 → promise2 → settimeout。

JavaScript 中的 Event Loop 是理解异步执行顺序的核心。面对经典的 setTimeout 与 Promise 混合调用面试题(比如包含同步代码、setTimeout、Promise.resolve().then() 等),关键不是死记“宏任务先于微任务”,而是看清**每个任务被推入哪个队列、何时被推入、以及当前执行栈清空后轮询的顺序**。
明确宏任务与微任务的来源
这是分析的第一步,不能混淆:
-
宏任务(Macrotask):全局脚本、
setTimeout、setInterval、setImmediate(Node.js)、I/O、UI 渲染等;每次 Event Loop 循环只执行一个宏任务。 -
微任务(Microtask):
Promise.then/catch/finally回调、MutationObserver、queueMicrotask();在当前宏任务结束后、下一个宏任务开始前,连续执行完所有微任务队列中的任务(直到队列为空)。
按代码执行流逐行追踪任务入队时机
不要跳读,从上到下模拟 JS 引擎执行过程,重点关注谁在什么时候把什么任务加进了哪个队列**:
- 同步代码立即执行(如
console.log('1')); - 遇到
setTimeout(fn, 0):立即注册定时器,但回调函数会被放入宏任务队列(即使延时为 0,也要等本轮宏任务+全部微任务完成后才可能执行); - 遇到
Promise.resolve().then(...):Promise 构造是同步的,.then回调会立即入微任务队列**(不是执行!只是入队); - 注意:
new Promise((resolve) => { console.log('sync'); resolve(); }).then(...)中,console.log是同步执行,.then回调仍入微任务队列。
记住 Event Loop 的标准循环步骤
每一轮循环严格遵循:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 从宏任务队列中取出第一个宏任务(如主脚本、某个 setTimeout 回调)执行;
- 执行过程中产生的微任务(如 new Promise.then、queueMicrotask)全部追加到微任务队列末尾**;
- 当前宏任务执行完后,立即、不间断地执行所有微任务**(先进先出),直到微任务队列为空;
- 渲染(可选,浏览器环境);
- 回到第 1 步,取下一个宏任务。
典型例题手撕(带注释)
以高频题为例:
console.log('script start');
setTimeout(() => console.log('setTimeout'), 0);
Promise.resolve()
.then(() => console.log('promise1'))
.then(() => console.log('promise2'));
console.log('script end');
执行顺序分析:
- 同步输出:
'script start'→'script end'; -
setTimeout回调入宏任务队列**; - 两个
.then回调依次入微任务队列**; - 第一轮宏任务(主脚本)结束 → 立即清空微任务队列 → 输出
'promise1'、'promise2'; - 微任务队列空 → 开始第二轮宏任务 → 执行
setTimeout回调 → 输出'setTimeout'。
最终输出:script start → script end → promise1 → promise2 → setTimeout。
不复杂但容易忽略细节。多练几道变体(比如嵌套 Promise、async/await、多个 setTimeout 延时不同),核心始终是:看入队类型、盯入队时机、按循环规则推演。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










