同步代码立即执行,微任务在同步后批量清空,宏任务按轮次依次执行;如console.log(1)、settimeout(() => console.log(2), 0)、promise.resolve().then(() => console.log(3))、queuemicrotask(() => console.log(4))、console.log(5)输出顺序为1→5→3→4→2。

要观察同步与异步在事件循环中的执行顺序,最直接的方式是组合使用 同步代码、setTimeout、Promise.then、queueMicrotask 等具有明确执行时机的 API,并按规范顺序输出日志。关键不是“看结果”,而是理解每类任务在事件循环各阶段的排队和执行位置。
同步代码立即执行,压栈即跑
同步语句(如 console.log、变量赋值、函数调用)在当前调用栈中逐行执行,不进入任务队列。它们构成宏任务的“主体部分”,也是所有异步回调的起点。
- 哪怕写在 setTimeout 回调里,里面的 console.log 仍是同步执行
- try/catch 只能捕获同步错误,无法捕获 Promise reject 或 setTimeout 抛出的异常
- 注意:return、throw、break 等控制流只影响当前同步上下文,不中断正在排队的微任务
微任务(Microtask)紧贴同步代码之后执行
Promise.then/catch/finally、queueMicrotask、MutationObserver 回调属于微任务,会在当前宏任务结束、渲染前,**清空整个微任务队列**(不是只执行一个)。
- 连续两个 Promise.resolve().then(),会按注册顺序全部执行完,中间不穿插其他宏任务
- 在 Promise.then 里再 new Promise 并 .then,新微任务会加入当前微任务队列尾部,仍会在本轮执行
- queueMicrotask(fn) 和 Promise.resolve().then(fn) 行为一致,但更轻量、无 Promise 创建开销
宏任务(Macrotask)按轮次排队,每次只执行一个
setTimeout、setInterval、I/O 回调、UI 渲染等属于宏任务。事件循环每次从宏任务队列取一个执行,执行完后立刻检查并清空微任务队列,再取下一个宏任务。
- 两个 setTimeout(..., 0) 不会连续执行;第一个执行完 → 清空微任务 → 第二个才开始执行
- requestAnimationFrame 在渲染前触发,但属于宏任务,优先级高于 setTimeout,但低于微任务
- 用户交互(如 click)产生的回调也是宏任务,和其他 setTimeout 同级排队
用一段典型代码验证执行顺序
运行以下代码,对照输出顺序,就能清晰看到各类型任务的落点:
console.log(1);setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
queueMicrotask(() => console.log(4));
console.log(5);
输出一定是:1 → 5 → 3 → 4 → 2。原因:
1 和 5 是同步;3 和 4 是微任务,在同步结束后立即批量执行;2 是下一个宏任务,在本轮事件循环最后才执行。











