JavaScript事件循环是宏任务与微任务交替执行的协作机制,微任务总在每次宏任务结束后立即连续执行;setTimeout等为宏任务,Promise.then等为微任务;await后代码是否异步取决于等待值类型。

JavaScript 事件循环不是“轮询检查”,也不是“定时扫描”,它是一套严格按规范执行的协作机制——宏任务与微任务交替清空,且微任务总在每次宏任务结束后立即、连续执行完毕。
宏任务和微任务怎么区分?哪些常见操作属于哪一类?
区分关键看规范定义和宿主环境实现,不是看“耗时长短”或“是否异步”。比如 setTimeout、setInterval、I/O 回调、UI 渲染 是宏任务;而 Promise.then/catch/finally、MutationObserver、queueMicrotask 是微任务。
-
Promise.resolve().then()会进微任务队列,哪怕里面是空函数或同步 throw -
setTimeout(() => {}, 0)一定进宏任务队列,即使延迟为 0,也要等当前任务+所有微任务跑完才执行 -
async function内部的await表达式后代码,本质被编译成Promise.then,所以也进微任务 -
requestIdleCallback是宏任务,不是微任务(常被误认)
为什么 await 后面的代码不总在下一个 tick 执行?
因为 await 的行为取决于它等待的值:如果等待的是已决议的 Promise(如 await Promise.resolve(42)),后续代码会作为微任务入队;但如果等待的是一个普通值(如 await 42),V8 会直接同步执行,不产生微任务。
- 等
Promise→ 微任务调度 → 下一轮微任务阶段执行 - 等非
Promise值(string、number、null)→ 无异步开销 → 同步继续 - 这个差异会导致调试时“有时快有时慢”,容易误判执行时机
Node.js 和浏览器的事件循环有啥实际差别?
Node.js 的事件循环有 6 个明确阶段(timers、pending callbacks、idle/prepare、poll、check、close callbacks),而浏览器没有公开阶段划分,只保证宏/微任务模型一致。最直接影响是:setImmediate 和 process.nextTick 在 Node.js 中存在,在浏览器中不存在且不可 polyfill。
立即学习“Java免费学习笔记(深入)”;
-
process.nextTick在当前操作结束、但还没离开当前阶段时执行,优先级高于所有微任务(包括Promise) -
setImmediate属于 check 阶段,总在 poll 阶段之后、下次事件循环开始前执行,优先级低于微任务 - 浏览器里想模拟
process.nextTick,只能用queueMicrotask,但它不等价——没有更高优先级
真正容易被忽略的是:事件循环本身不“管理”任务队列,它只是按规则消费队列;而队列由 JavaScript 引擎(V8、SpiderMonkey)和宿主环境(Chrome、Node.js)共同维护。改写一个 Promise 实现不会改变微任务调度逻辑,但替换掉 setTimeout 的底层绑定,就可能破坏宏任务顺序。











