<!-- title: 卡顿从哪来：事件循环神课深读 -->
<!-- domain: programming -->

# 卡顿从哪来：一场把 JavaScript 讲透的传奇演讲深度解读

## 概述

在 JavaScript 的学习路径上，有一个几乎人人绕不过去的名字：Philip Roberts 在 JSConf EU 2014 上的演讲 **《What the heck is the event loop anyway?》（事件循环到底是什么鬼？）**。这场不到 30 分钟的演讲，用一页著名的示意图和几行伪代码，把“单线程的 JavaScript 为什么能处理异步”这道几乎所有前端工程师都似懂非懂的问题，讲成了十年后仍在被反复引用的经典。本文以这场演讲为唯一对象，完整还原它的论证脉络、背后的工程与规范溯源，并用今天（2026 年）的眼光审视哪些结论依然坚挺、哪些需要修正——让你无论是否看过原视频，都能把“事件循环”这个概念真正装进自己的认知框架里。

## 一、视频基本信息

| 项目 | 内容 |
|------|------|
| 视频标题 | What the heck is the event loop anyway? |
| 中文译名 | 事件循环到底是个什么东西？ |
| 演讲者 | Philip Roberts（时任 And Yet 工程师，Loupe 可视化工具作者） |
| 频道 / 活动 | JSConf（欧洲 JavaScript 大会 JSConf EU 2014 柏林站官方频道） |
| 发布时间 | 2014 年（演讲年份；据 JSConf 官方频道） |
| 热度 | 长期位居该主题全球播放量前列，累计播放达数百万量级（本次写作环境无法联网，未能实时核实最新数字，以频道页面为准） |
| 时长 | 约 26 分钟 |

> 说明：本文写作环境无法访问网络，以上信息来自演讲与频道的公开资料，未做实时二次核实。文中不引用未经核实的精确数字。

## 二、核心主张拆解：演讲到底讲了什么

这场演讲的结构堪称技术演讲的范本：从一个让全场发笑的问题开场，逐层拆解，最后用一个工具收束全场。我们按叙事顺序完整还原它的五步论证。

### 2.1 开场之问：“你们真的懂 JavaScript 吗？”

Philip 开场抛出的挑衅式问题是：我们都听说过“JavaScript 是单线程的”“回调、setTimeout 是异步的”“Node.js 非阻塞无阻塞 IO”这些说法，但如果你问他“事件循环是什么”，很多人只能含糊地比划。他给出的判断是：**理解事件循环，是理解 JavaScript 运行时行为的分水岭**——不懂它，你写的每一行异步代码都在“碰运气”。

这个开场策略很高明：它没有从 API 或语法入手，而是直指一个心智模型（mental model）的缺失。绝大多数开发者会用异步代码，但解释不了“浏览器是如何在只有一个线程的情况下，同时监听点击、发起网络请求、又执行回调的”。

### 2.2 调用栈：程序的心电图

演讲的第一个技术支点是**调用栈（Call Stack）**。Philip 把它形容为 JavaScript 引擎里“记录我们执行到哪了”的数据结构：每调用一个函数，就压入（push）一个栈帧；函数返回，就弹出（pop）。

这里他演示了两个经典片段：

- **栈展开（stack trace）**：程序报错时打印的那串调用链，本质上就是异常发生瞬间调用栈的快照；
- **栈溢出（stack overflow）**：递归没有终止条件时，栈帧无限压入，直到引擎抛出 `RangeError: Maximum call stack size exceeded`。

关键结论随之而来：**JavaScript 的调用栈一次只能运行一个函数，因为栈是串行的**。“单线程”这个词在演讲里被精确定义为“一个调用栈”——不是玄学，而是一个数据结构的物理约束。

### 2.3 “阻塞”到底阻塞了什么：掉帧的真相

接着 Philip 引入全场最重要的一段推理：既然只有一个栈，那么当栈里有一个耗时的函数在跑时，**其他任何事都做不了**。

他举的场景是图片解码、网络请求被同步执行、或者一个笨重的循环——如果这些代码在浏览器主线程上同步运行 300 毫秒，那么这 300 毫秒里：渲染不能发生、点击无法响应、动画停摆。这就是用户感知到的“卡死”。

演讲在此给出了一个量化锚点：显示器通常以 60fps 刷新，意味着浏览器每帧只有约 **16.7 毫秒**的预算去完成“跑 JS → 计算样式 → 布局 → 绘制”的全部工作。任何一段超过这个预算的同步代码，都会直接表现为掉帧。这个数字在后来的 Web 性能工程里被 Google 的 RAIL 模型正式化（响应 100ms 内、动画帧 16ms 内，参见 web.dev 的 RAIL 文档）。

### 2.4 浏览器不止 JavaScript：WebAPIs 与任务队列

然后是全场最著名的转折：**那些“异步”的东西，根本不在 JavaScript 引擎里**。

Philip 指出，浏览器提供的 `setTimeout`、DOM 事件、`XMLHttpRequest`（以及今天的 fetch），这些能力属于**浏览器 WebAPIs**——它们由浏览器实现，调用时浏览器接管任务，JavaScript 调用栈继续往下走。等浏览器那边的事情完成（定时器到点、请求返回、事件触发），回调函数并不会直接冲进调用栈，而是先被放进**任务队列（Task Queue，演讲中称 callback queue）**排队。

### 2.5 事件循环：真正的主角只有几行代码

终于到了标题里的主角。Philip 给出的定义至今仍被无数次引用，它的伪代码大致是：

```javascript
while (queue.waitForMessage()) {
  queue.processNextMessage();
}
```

翻译成人话：**事件循环就是当调用栈为空时，把任务队列里的第一个回调取出来，压到栈上执行的守门人**。它没有魔法，就是一个永不停歇的 while 循环。

由此他给出了那个改变无数人认知的结论：**`setTimeout(fn, 0)` 并不意味着“0 毫秒后执行”**。它只承诺“至少 0 毫秒后，回调会进入任务队列”；至于它什么时候真正执行，完全取决于调用栈什么时候清空。如果栈上有一段跑 5 秒的代码，你的 `setTimeout(fn, 0)` 就会迟到 5 秒。演讲现场他用一段代码当场验证了这一点，全场安静的那几秒正是这个演讲名场面。

### 2.6 收束工具：Loupe，让异步看得见

演讲的最后，Philip 演示了他为这场演讲构建的工具 **Loupe**（可在 latentflip.com/loupe 直接使用，代码开源）：把任意 JS 代码粘贴进去，它会逐步可视化“调用栈压入弹出 → WebAPI 注册 → 回调入队 → 事件循环搬运”的全过程，还能插入 `console.log` 观察输出顺序。这套可视化后来成为大量讲师复用的教学素材——你不是在“背”事件循环，而是能亲眼看一遍它。

## 三、知识与理论溯源：这些说法都从哪来

这场演讲不是发明了事件循环，而是把散落在规范、引擎和操作系统里的既有事实第一次串成了普通人能吸收的叙事。它的每一个支点都有真实出处。

**调用栈与运行时模型**：来自 ECMAScript 规范定义的执行上下文机制（execution context），MDN 的《Concurrency model and the event loop》文档对其有系统总结：JavaScript 拥有一个调用栈、一个堆，以及由运行时环境（浏览器/Node）提供的队列。这是“单线程”表述的规范学根源。

**WebAPIs 与事件循环本身**：浏览器行为由 WHATWG HTML Living Standard 的 *Event Loops* 章节规定——注意是复数，规范明确每个浏览器可以（在某些条件下必须有，如 Web Worker）多个事件循环，并定义了 task queue 与后续标准化的 microtask queue。Philip 演讲中的简化模型与规范条款高度对应，只是做了教学裁剪。

**`setTimeout` 的最小延迟条款**：HTML 规范规定，嵌套超过 5 层的 `setTimeout`，最小延迟被钳制（clamp）为 4ms——这是“定时器精度不可信”的规范学证据，比演讲更进一步。

**Node.js 一侧的对应物**：Ryan Dahl 在 2009 年创建 Node.js 时，把这套“非阻塞 + 事件循环”的模型搬到了服务端，底层依赖 **libuv** 库实现事件循环与异步 IO（libuv 起源于 Node.js 项目，后来成为独立的跨平台异步 IO 库）。Node 官方文档《The Node.js Event Loop, Timers, and process.nextTick()》明确描述了其事件循环的六个阶段（timers、pending callbacks、idle/prepare、poll、check、close callbacks），与浏览器模型同源但细节不同。

**帧预算与性能工程**：演讲中“16.7ms”的直觉判断，后来被 Google 在 RAIL 性能模型中正式化，并衍生出 Long Tasks API（探测超过 50ms 的主线程长任务）与 web.dev 上一系列“拆分长任务”的工程指南。

**微任务的补全**：演讲时代的观众还看不到完整图景——Promise 直到 ES2015 才被标准化，随之而来的是比 task queue 优先级更高的**微任务队列（microtask queue）**。把这块拼图补全的标志性演讲是 Chrome 团队 Jake Archibald 2018 年的《In The Loop》（JSConf.Asia）：微任务在**每个宏任务执行完毕后立即整体清空**，然后才可能轮到渲染。两场演讲相隔四年，合起来才是 2026 年完整的事件循环知识体系。

## 四、批判性审查：哪些站得住，哪些要修正

**证据充分、至今坚挺的部分**：

1. **单调用栈模型与阻塞成因**。主线程一次跑一段代码、长任务阻塞渲染，这在 2026 年的浏览器里依然完全成立，是 web.dev、Chrome DevTools 文档反复强调的第一性能定律。
2. **`setTimeout(fn, 0)` 的语义**。“至少延迟，不保证准时”不仅成立，规范还加了 4ms 嵌套钳制，实际工程中定时器精度还要叠加页面后台化时的节流（浏览器会把后台标签页的定时器节流到每秒 1 次左右）。
3. **“事件循环是简单循环”的教学模型**。作为心智模型它极度有效，Loupe 的可视化至今仍是讲解该主题的标准工具。

**需要修正或补充的部分**：

1. **“JavaScript 是单线程”这句话在今天要精确理解**。自 ES2017 引入 `async/await`、浏览器普遍支持 Web Workers、Node 提供 `worker_threads`、`SharedArrayBuffer` 与 `Atomics` 允许真正的内存共享并发之后，“语言单线程”的说法已不准确——准确表述是：**默认的 JS 执行环境（主线程）是一个事件循环驱动的单线程，并行能力由宿主环境的额外线程提供**。演讲完成于 2014 年，彼时这样讲没有问题，今天直接复述就要加限定词。
2. **演讲简化掉了微任务队列**。这不能苛责（Promise 尚未标准化），但今天若只按演讲的“一个队列”模型推理 `Promise.then` 与 `setTimeout` 的顺序，会得出错误结论。正确顺序是：同步代码 → 清空全部微任务 → （必要时渲染）→ 取一个宏任务 → 再清空微任务，如此往复。
3. **Node 与浏览器的事件循环并不完全一样**。演讲以浏览器为语境，若把模型原样套到 Node 上会踩坑：Node 的循环有明确阶段划分，`process.nextTick` 队列优先级甚至高于 Promise 微任务；且 Node 11（2018 年）曾专门调整微任务的执行时机以向浏览器对齐——这类“运行时差异”恰恰说明把教学模型当生产模型是有代价的。
4. **“渲染每帧 16.7ms”是 60Hz 显示器的特例**。2026 年高刷新率屏幕（120Hz、144Hz、240Hz）普及，帧预算相应缩短到 8.3ms 甚至更少，"16.7ms"应理解为“按设备刷新率计算的帧预算”这一公式，而非固定常数。

总体评价：这是一篇**证据密度极高、几乎零事实错误**的演讲，它被引用十余年不是情怀，而是因为它的简化模型在“教学正确性”与“工程可用性”之间取到了罕见的平衡。需要修正的四点全部属于“2014 年之后世界变了”的范畴，而非演讲本身的疏漏。

## 五、认知提升与实用启示：三条可以立刻执行的建议

**第一，用“帧预算”重估你的每一行同步代码。** 写代码时默认这样一个预算表：以你的目标设备刷新率去除 1000ms，得到每帧预算（60Hz 约 16.7ms，120Hz 约 8.3ms）。任何预估超过 50ms 的同步逻辑（大数组循环、同步 JSON 解析、大量 DOM 操作）都应视为“会卡”的代码。落地手段：把长循环切片，用 `setTimeout`/`MessageChannel`/`scheduler.yield()`（较新的浏览器已支持）把控制权交还事件循环，让出间隙给渲染与输入响应。

**第二，把“异步”和“并行”在脑中彻底分开。** 这场演讲最大的认知红利就在于此：`setTimeout`、Promise、事件回调全部是**排队机制**，不是并行机制——排队不省 CPU，只是换一个执行时机。真正并行的是 Web Workers / `worker_threads`。当你需要“不卡主线程的计算”（如大文件哈希、图像处理），答案不是更花哨的异步写法，而是把工作移出主线程；当你需要“延后但不占资源”，才是异步回调。先用这个二分法定性需求，再选工具，能避开大量“为什么还是卡”的无效调优。

**第三，用工具建立直觉，再回到真实代码验证。** 花二十分钟在 Loupe（latentflip.com/loupe）里跑三段代码：一段 `setTimeout(fn,0)` 排队实验、一段嵌套 Promise 与 setTimeout 的混合输出、一段故意死循环阻塞栈的实验——把输出顺序亲手“看”一遍。然后打开 Chrome DevTools 的 Performance 面板录制一次真实页面交互，找到主线程上超过 50ms 的黄色长任务，对照理解“掉帧”和演讲中“栈被占用”的对应关系。概念只有在两个工具之间往返一次，才会变成可迁移的直觉。

## 六、论证结构图

```mermaid
flowchart TD
    A["开场之问<br/>你真的懂事件循环吗"] --> B["调用栈 Call Stack<br/>一次只跑一个函数"]
    B --> C["阻塞的代价<br/>栈被占用 → 渲染停摆/掉帧<br/>帧预算约16.7ms(60Hz)"]
    C --> D["WebAPIs<br/>setTimeout / DOM事件 / 网络请求<br/>在引擎之外执行"]
    D --> E["任务队列 Task Queue<br/>完成的回调在此排队"]
    E --> F["事件循环 Event Loop<br/>栈空?→取队首回调压栈<br/>一个while循环而已"]
    F --> G{"推论"}
    G --> H["setTimeout(fn,0)<br/>≠ 立即执行<br/>只保证'至少0ms后入队'"]
    G --> I["同步长代码<br/>会让一切回调迟到"]
    H --> J["工具收束<br/>Loupe 可视化验证"]
    I --> J
    J -.2014年后的补充.-> K["微任务队列<br/>Promise → 每个宏任务后整体清空"]
    J -.2014年后的补充.-> L["真正的并行<br/>Web Workers / worker_threads"]
```

## 结语

一场 2014 年的演讲能在 2026 年仍被反复提及，靠的不是流行度，而是它击中了一个长久没有好教材的断层：**从“会写异步代码”到“理解异步为何这样运行”之间的鸿沟**。Philip Roberts 的贡献在于，他把规范条文翻译成了一个只有四个组件的模型——栈、WebAPIs、队列、循环——小到可以装进一张示意图，准到可以预测 `setTimeout` 与 `console.log` 的输出顺序。如果你只打算深读一个 JavaScript 主题的视频，它仍然是这个物种里的标杆；读完本文后配合 Loupe 做一次亲手实验，你对“卡顿从哪来”的理解，将超过绝大多数只会背八股的同侪。

## 延伸阅读

1. Jake Archibald —《In The Loop》（JSConf.Asia 2018，YouTube 可搜）— 本文的官方续篇：微任务队列、渲染时机、task 与 microtask 的完整执行顺序，看完本文后必看。
2. MDN 文档 —《Concurrency model and the event loop》— 事件循环的规范级摘要，适合作为案头手册随时查阅。

## 参考来源

1. [What the heck is the event loop anyway? — Philip Roberts, JSConf EU 2014](https://www.youtube.com/watch?v=8aGhZQkoFbQ) — 本文解读的原视频，JSConf 官方频道。
2. [MDN: Concurrency model and the event loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop) — 调用栈、堆、队列模型的规范级说明。
3. [WHATWG HTML Living Standard — Event Loops](https://html.spec.whatwg.org/multipage/webappapis.html#event-loops) — task queue 与事件循环的权威规范定义。
4. [Loupe — Philip Roberts 的异步可视化工具](http://latentflip.com/loupe) — 演讲现场演示的教学工具，可在线运行。
5. [Jake Archibald: In The Loop — JSConf.Asia 2018](https://www.youtube.com/watch?v=cCOL7MC4Pl0) — 微任务队列的权威补充演讲。

---
> 本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵**爱卫生**（微信ID: gprshome201101）维护，聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。
> 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。
