事件循环按同步代码→微任务→宏任务的确定规则调度任务;同步代码严格按顺序执行;微任务在每个宏任务结束后立即全部执行;宏任务如setTimeout按注册顺序和延迟时间排队执行。

事件循环本身不“保证”异步代码按你的书写顺序执行,而是按一套确定的规则调度任务——理解这套规则,你才能让异步代码按预期顺序执行。
同步代码优先执行
所有同步语句会从上到下、依次压入调用栈并立即执行。这是整个顺序的起点,也是最确定的部分。只要没被异步逻辑打断,控制台输出、变量赋值、函数调用都会严格按代码顺序发生。
微任务总在宏任务之间“插队”
当一个宏任务(比如整体脚本、setTimeout 回调)执行完毕,调用栈清空后,事件循环不会马上取下一个宏任务,而是先检查微任务队列,并把里面所有任务一次性执行完。
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- Promise.then/catch/finally、queueMicrotask、MutationObserver 都属于微任务
- 哪怕有 10 个 Promise.then,它们也会在当前宏任务结束后立刻连续执行,中间不会插入任何宏任务
- 这解释了为什么 Promise 回调总比 setTimeout 回调早输出,哪怕 setTimeout 写在前面
宏任务按队列顺序逐个执行
宏任务包括:script(初始脚本)、setTimeout、setInterval、I/O 回调、UI 渲染等。事件循环每轮只从中取出一个执行,执行完再清空微任务,然后进入下一轮。
- setTimeout(fn, 0) 不是“立刻”,只是尽快排进宏任务队列,仍需等待当前宏任务结束 + 微任务清空 + 下一轮循环
- 多个 setTimeout 的执行顺序取决于它们注册完成的时间点和延迟时间,不是代码位置
- requestAnimationFrame 是特殊的宏任务,浏览器会在下一帧渲染前执行,适合动画控制
async/await 是 Promise 的语法糖,本质仍是微任务
async 函数返回 Promise,await 后的代码会被包装成 .then 回调,因此也进入微任务队列。
- await 后面的表达式一旦 resolve,后续语句就作为微任务排队
- 连续 await 并不会“阻塞”主线程,而是把每个后续步骤变成链式微任务
- 想让某段逻辑延后到下一个宏任务?可以用 setTimeout(() => {}, 0) 或直接写在下一个 .then 里










