javascript事件循环是单线程下协调同步与异步任务的调度机制,依赖调用栈、微任务队列和宏任务队列协同工作:同步代码入栈执行,栈空后清空微任务队列,再取一个宏任务执行,循环往复。

JavaScript 的事件循环本质是单线程下协调同步与异步任务的调度机制,核心就靠一个栈 + 两个队列配合运转:调用栈负责执行,微任务队列和宏任务队列负责排队,事件循环本身不干活,只做“判空→取任务→推入”的搬运工。
调用栈:唯一且即时的执行通道
它是一个后进先出(LIFO)结构,所有同步代码都直接压进去执行。函数调用形成栈帧,执行完立刻弹出。只要栈里还有东西,其他任何任务都得等着——这也是为什么一段死循环或超长计算会让页面卡死:栈被占满,事件循环无法启动。
- 每次函数调用都会新增一个栈帧,包括匿名函数、箭头函数、立即执行函数
- 栈深度有上限(如 V8 默认约 10000 层),超出会抛出 RangeError: Maximum call stack size exceeded
- 异步回调不会直接进栈;它们必须等事件循环判断栈空后,才从队列取出并推入
微任务队列:每轮循环必清空的高优队列
它不是 FIFO 队列的简单排队,而是“本轮同步代码结束后、下一个宏任务开始前”的强制清空区。Promise.then、queueMicrotask、MutationObserver 回调都进这里,优先级高于一切宏任务。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 哪怕在 setTimeout 回调里 new Promise,它的 then 也会插队到当前宏任务末尾、下一个宏任务之前
- 微任务可以无限递归触发新微任务(比如 then 里再调用 queueMicrotask),但浏览器会限制嵌套深度以防饿死主线程
- 注意:await 后的代码本质是包装成 Promise.resolve().then(),所以也属于微任务
宏任务队列:按顺序逐个取出的主干队列
它存放的是真正意义上的“异步任务入口”,比如 script 标签整体、setTimeout、setInterval、I/O 完成、用户点击事件、fetch 响应等。事件循环每轮只从中取一个执行。
- 多个 setTimeout(0) 并不保证执行顺序完全一致,因为它们进入队列的时间点受宿主环境调度影响,但先进入的大概率先执行
- UI 渲染(如重排重绘)通常夹在“清空微任务”和“取下一个宏任务”之间,这就是为什么 Promise.then 比 setTimeout 更快响应数据变化
- Node.js 中 process.nextTick 是比 Promise.then 还早的微任务变体,但仅限服务端使用
事件循环的真实执行节奏
不是“先微后宏”一句话能概括的,而是一次完整的循环包含固定步骤:
- 执行当前宏任务中的全部同步代码(可能触发新宏/微任务)
- 同步代码结束,检查并一次性执行完所有已排队的微任务(中途不穿插宏任务)
- (浏览器)可选地进行 UI 渲染
- 从宏任务队列头部取出一个任务,开始下一轮循环
这个节奏决定了:连续 10 个 Promise.then 会一口气跑完;但 10 个 setTimeout(0) 会被拆成 10 轮循环,中间穿插渲染和其他宏任务。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










