<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Worker Pool on 吾爱主机</title><link>https://blog.waihost.com/tags/worker-pool/</link><description>Recent content in Worker Pool on 吾爱主机</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Thu, 23 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://blog.waihost.com/tags/worker-pool/index.xml" rel="self" type="application/rss+xml"/><item><title>Node.js 事件循环与 nextTick / microtask：原理、阶段与工程实践</title><link>https://blog.waihost.com/posts/nodejs-event-loop-nexttick-microtask/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/nodejs-event-loop-nexttick-microtask/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/nodejs-event-loop-nexttick-microtask.svg" alt="Featured image of post Node.js 事件循环与 nextTick / microtask：原理、阶段与工程实践" /&gt;&lt;p&gt;HTTP 服务偶发“全站卡住几秒”、定时器比预期晚很多、&lt;code&gt;setTimeout(0)&lt;/code&gt; 和 &lt;code&gt;setImmediate&lt;/code&gt; 顺序飘忽不定、同步校验回调却读到未初始化变量——这些现象往往不是“Node 慢”，而是对 &lt;strong&gt;事件循环（Event Loop）阶段、nextTick 队列、微任务队列、Worker Pool&lt;/strong&gt; 理解不到位。&lt;/p&gt;
&lt;p&gt;本文基于 Node.js 官方 Learn 文档 &lt;em&gt;The Node.js Event Loop&lt;/em&gt;、&lt;em&gt;Don&amp;rsquo;t Block the Event Loop&lt;/em&gt;，以及 API 文档 &lt;code&gt;process.nextTick&lt;/code&gt; / Timers，把调度机制讲清楚，并给出可复现实验与排障清单。&lt;/p&gt;
&lt;h2 id="一问题背景单线程-js-为何能扛大量并发"&gt;&lt;a href="#%e4%b8%80%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af%e5%8d%95%e7%ba%bf%e7%a8%8b-js-%e4%b8%ba%e4%bd%95%e8%83%bd%e6%89%9b%e5%a4%a7%e9%87%8f%e5%b9%b6%e5%8f%91" class="header-anchor"&gt;&lt;/a&gt;一、问题背景：单线程 JS 为何能扛大量并发
&lt;/h2&gt;&lt;p&gt;官方对事件循环的定位很直接：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;The event loop is what allows Node.js to perform &lt;strong&gt;non-blocking I/O&lt;/strong&gt; operations — despite the fact that a single JavaScript thread is used by default — by offloading operations to the system kernel whenever possible.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;也就是说：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;应用层 JS 默认跑在一条线程&lt;/strong&gt;（Event Loop / main thread）。&lt;/li&gt;
&lt;li&gt;能丢给内核的非阻塞 I/O（如多数网络 I/O）完成后，内核通知 Node，相关回调进入 &lt;strong&gt;poll&lt;/strong&gt; 等队列等待执行。&lt;/li&gt;
&lt;li&gt;不便做成纯非阻塞的昂贵工作（部分 &lt;code&gt;fs&lt;/code&gt;、&lt;code&gt;dns.lookup&lt;/code&gt;、&lt;code&gt;crypto.pbkdf2&lt;/code&gt;、&lt;code&gt;zlib&lt;/code&gt; 等）会提交到 libuv 的 &lt;strong&gt;Worker Pool&lt;/strong&gt;，做完再回到事件循环。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Don&amp;rsquo;t Block the Event Loop&lt;/em&gt; 给出的工程拇指法则是：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Node.js is fast when the work associated with each client at any given time is &lt;strong&gt;&amp;ldquo;small&amp;rdquo;&lt;/strong&gt;.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;线程少、上下文切换少，是 Node 能用较少资源服务大量连接的原因；代价是：&lt;strong&gt;任意一次过长的同步回调，都会让其他客户端得不到轮转&lt;/strong&gt;——既伤吞吐，也构成 DoS 面（恶意大输入、灾难性正则等）。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;线程类型&lt;/th&gt;
					&lt;th&gt;跑什么&lt;/th&gt;
					&lt;th&gt;阻塞后果&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Event Loop&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;初始化、JS 回调、&lt;code&gt;await&lt;/code&gt;/&lt;code&gt;then&lt;/code&gt; 续体、网络等非阻塞 I/O 编排&lt;/td&gt;
					&lt;td&gt;所有请求排队；定时器/I/O 回调整体延迟&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Worker Pool&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;昂贵任务（多数 &lt;code&gt;fs&lt;/code&gt;、部分 DNS/Crypto/Zlib、C++ 提交的任务）&lt;/td&gt;
					&lt;td&gt;池子被占满时同类任务排队；回压到业务&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="二核心模型阶段队列--阶段外的-nexttick"&gt;&lt;a href="#%e4%ba%8c%e6%a0%b8%e5%bf%83%e6%a8%a1%e5%9e%8b%e9%98%b6%e6%ae%b5%e9%98%9f%e5%88%97--%e9%98%b6%e6%ae%b5%e5%a4%96%e7%9a%84-nexttick" class="header-anchor"&gt;&lt;/a&gt;二、核心模型：阶段队列 + 阶段外的 nextTick
&lt;/h2&gt;&lt;h3 id="1-启动与相位图"&gt;&lt;a href="#1-%e5%90%af%e5%8a%a8%e4%b8%8e%e7%9b%b8%e4%bd%8d%e5%9b%be" class="header-anchor"&gt;&lt;/a&gt;1. 启动与相位图
&lt;/h3&gt;&lt;p&gt;进程启动后会：初始化事件循环 → 执行入口脚本（其中可调异步 API、定时器、&lt;code&gt;process.nextTick()&lt;/code&gt;）→ 再进入循环迭代。&lt;/p&gt;
&lt;p&gt;官方简化相位（每个 box 是一个 &lt;strong&gt;phase&lt;/strong&gt;）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ timers │ setTimeout / setInterval
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─────────────┬─────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌─&amp;gt;│ pending callbacks │ 延迟到下一轮的部分系统 I/O 回调
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └─────────────┬─────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ idle, prepare （内部使用）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ poll 取新 I/O 事件；执行 I/O 回调
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ check setImmediate 回调
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ close callbacks 如 socket.on(&amp;#39;close&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└───────────── timers ──────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一般规则：进入某 phase 后，先做该 phase 专属操作，再 FIFO 执行队列回调，直到队列耗尽或达到系统相关的上限，再进入下一 phase。&lt;/p&gt;
&lt;p&gt;文档还强调：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;timers&lt;/strong&gt; 指定的是“阈值（threshold）之后&lt;strong&gt;尽早&lt;/strong&gt;可执行”，不是精确闹钟；OS 调度与其它回调都会让它推迟。&lt;/li&gt;
&lt;li&gt;从 &lt;strong&gt;libuv 1.45.0（Node.js 20）&lt;/strong&gt; 起，&lt;strong&gt;每个事件循环迭代中 timers 在 poll 之后运行&lt;/strong&gt;；为兼容，进入循环前仍会跑一轮 timers。这会影响某些场景下 &lt;code&gt;setImmediate&lt;/code&gt; 与定时器的相对时序。&lt;/li&gt;
&lt;li&gt;每一轮事件循环之间，若不再等待任何异步 I/O 或定时器，进程可干净退出。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-各-phase-在工程上的含义"&gt;&lt;a href="#2-%e5%90%84-phase-%e5%9c%a8%e5%b7%a5%e7%a8%8b%e4%b8%8a%e7%9a%84%e5%90%ab%e4%b9%89" class="header-anchor"&gt;&lt;/a&gt;2. 各 phase 在工程上的含义
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Phase&lt;/th&gt;
					&lt;th&gt;主要职责&lt;/th&gt;
					&lt;th&gt;工程注意&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;timers&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;setTimeout&lt;/code&gt; / &lt;code&gt;setInterval&lt;/code&gt; 到期回调&lt;/td&gt;
					&lt;td&gt;阈值语义；可能被长回调推迟&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;pending callbacks&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;部分系统操作延迟回调（如某些 TCP &lt;code&gt;ECONNREFUSED&lt;/code&gt;）&lt;/td&gt;
					&lt;td&gt;平台差异存在，但日常业务少直接依赖&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;poll&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;检索新 I/O；执行几乎所有 I/O 回调（除 close、定时器、&lt;code&gt;setImmediate&lt;/code&gt;）&lt;/td&gt;
					&lt;td&gt;空队列时可能阻塞等待；有 &lt;code&gt;setImmediate&lt;/code&gt; 时可能结束 poll 进入 check&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;check&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;setImmediate&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;“poll 完成后立即跑一轮脚本”的专用相位&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;close callbacks&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;如 &lt;code&gt;socket.destroy()&lt;/code&gt; 触发的 &lt;code&gt;'close'&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;否则 close 也可能经 &lt;code&gt;nextTick&lt;/code&gt; 发出&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="3-processnexttick-不在相位图里"&gt;&lt;a href="#3-processnexttick-%e4%b8%8d%e5%9c%a8%e7%9b%b8%e4%bd%8d%e5%9b%be%e9%87%8c" class="header-anchor"&gt;&lt;/a&gt;3. &lt;code&gt;process.nextTick&lt;/code&gt; 不在相位图里
&lt;/h3&gt;&lt;p&gt;官方明确：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;code&gt;process.nextTick()&lt;/code&gt; is &lt;strong&gt;not technically part of the event loop&lt;/strong&gt;. Instead, the &lt;strong&gt;nextTickQueue&lt;/strong&gt; will be processed after the current operation is completed, &lt;strong&gt;regardless of the current phase&lt;/strong&gt;.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;任意 phase 里调用 &lt;code&gt;nextTick&lt;/code&gt;，都会在&lt;strong&gt;当前操作结束后、事件循环继续前进前&lt;/strong&gt;排空 nextTick 队列。因此递归 &lt;code&gt;nextTick&lt;/code&gt; 可以 &lt;strong&gt;饿死 I/O&lt;/strong&gt;（永远到不了 poll）——这是合法但危险的行为。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;process&lt;/code&gt; API 进一步说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nextTick(callback)&lt;/code&gt; 把回调放进 &lt;strong&gt;next tick queue&lt;/strong&gt;；当前 JS 栈操作结束后、事件循环继续前&lt;strong&gt;完全排空&lt;/strong&gt;该队列。&lt;/li&gt;
&lt;li&gt;稳定性标注为 &lt;strong&gt;Legacy&lt;/strong&gt;，多数用户代码更推荐 &lt;strong&gt;&lt;code&gt;queueMicrotask()&lt;/code&gt;&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;每次 next tick 队列排空后，会&lt;strong&gt;立即&lt;/strong&gt;再排空 &lt;strong&gt;microtask 队列&lt;/strong&gt;（Promise 的 &lt;code&gt;then&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt;/&lt;code&gt;finally&lt;/code&gt;、&lt;code&gt;queueMicrotask&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模块系统差异&lt;/strong&gt;（官方示例）：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CJS&lt;/strong&gt;：通常 &lt;code&gt;nextTick&lt;/code&gt; → Promise/&lt;code&gt;queueMicrotask&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ESM&lt;/strong&gt;：模块求值本身已在 microtask 路径上，常见顺序是 Promise/&lt;code&gt;queueMicrotask&lt;/code&gt; → &lt;code&gt;nextTick&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// CJS 常见输出：nextTick → resolve → microtask
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nextTick&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;node:process&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;resolve&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;queueMicrotask&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;microtask&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;nextTick&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;nextTick&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ESM 常见输出：resolve → microtask → nextTick
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nextTick&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;node:process&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;resolve&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;queueMicrotask&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;microtask&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;nextTick&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;nextTick&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="4-setimmediate-vs-settimeout0"&gt;&lt;a href="#4-setimmediate-vs-settimeout0" class="header-anchor"&gt;&lt;/a&gt;4. &lt;code&gt;setImmediate&lt;/code&gt; vs &lt;code&gt;setTimeout(0)&lt;/code&gt;
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;API&lt;/th&gt;
					&lt;th&gt;语义（官方）&lt;/th&gt;
					&lt;th&gt;典型落点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;setImmediate(fn)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;poll 阶段结束后执行脚本&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;check&lt;/strong&gt; phase&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;setTimeout(fn, 0)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;至少等待阈值（0ms）后尽早执行&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;timers&lt;/strong&gt; phase&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;主模块顶层&lt;/strong&gt;同时调度两者：顺序&lt;strong&gt;不确定&lt;/strong&gt;，取决于进程性能与调度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在 I/O 回调内&lt;/strong&gt;（例如 &lt;code&gt;fs.readFile&lt;/code&gt; 回调里）同时调度：官方保证 &lt;strong&gt;&lt;code&gt;setImmediate&lt;/code&gt; 总是先于 timers&lt;/strong&gt;，与 timers 数量无关。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;官方也坦言命名历史包袱：&lt;code&gt;nextTick&lt;/code&gt; 比 &lt;code&gt;setImmediate&lt;/code&gt; 更“立即”，名字几乎应互换；但为兼容 npm 生态不会改名。&lt;strong&gt;一般推荐优先用 &lt;code&gt;setImmediate&lt;/code&gt; 推理成本更低&lt;/strong&gt;；&lt;code&gt;nextTick&lt;/code&gt; 留给“必须在调用栈展开后、循环前进前”的场景。&lt;/p&gt;
&lt;h2 id="三实践可复现实验"&gt;&lt;a href="#%e4%b8%89%e5%ae%9e%e8%b7%b5%e5%8f%af%e5%a4%8d%e7%8e%b0%e5%ae%9e%e9%aa%8c" class="header-anchor"&gt;&lt;/a&gt;三、实践：可复现实验
&lt;/h2&gt;&lt;h3 id="实验-aio-周期内-immediate-恒先于-timeout0"&gt;&lt;a href="#%e5%ae%9e%e9%aa%8c-aio-%e5%91%a8%e6%9c%9f%e5%86%85-immediate-%e6%81%92%e5%85%88%e4%ba%8e-timeout0" class="header-anchor"&gt;&lt;/a&gt;实验 A：I/O 周期内 immediate 恒先于 timeout(0)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// save as io-order.js (CJS)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;node:fs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;timeout&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;setImmediate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;immediate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node io-order.js
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 期望：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# immediate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# timeout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="实验-bnexttick-在当前操作后插入早于进入下一-phase"&gt;&lt;a href="#%e5%ae%9e%e9%aa%8c-bnexttick-%e5%9c%a8%e5%bd%93%e5%89%8d%e6%93%8d%e4%bd%9c%e5%90%8e%e6%8f%92%e5%85%a5%e6%97%a9%e4%ba%8e%e8%bf%9b%e5%85%a5%e4%b8%8b%e4%b8%80-phase" class="header-anchor"&gt;&lt;/a&gt;实验 B：nextTick 在“当前操作后”插入，早于进入下一 phase
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// nexttick-vs-immediate.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;node:fs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;setImmediate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;immediate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextTick&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;nextTick&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;promise&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 I/O 回调这个“当前操作”末尾，会先排空 &lt;strong&gt;nextTick&lt;/strong&gt;，再处理 microtask，然后事件循环才能推进到后续 phase（check 中的 immediate）。你应看到 &lt;code&gt;nextTick&lt;/code&gt; 先于 &lt;code&gt;immediate&lt;/code&gt;；CJS 下 &lt;code&gt;nextTick&lt;/code&gt; 通常也先于 &lt;code&gt;promise&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="实验-c为何-api-必须全同步或全异步"&gt;&lt;a href="#%e5%ae%9e%e9%aa%8c-c%e4%b8%ba%e4%bd%95-api-%e5%bf%85%e9%a1%bb%e5%85%a8%e5%90%8c%e6%ad%a5%e6%88%96%e5%85%a8%e5%bc%82%e6%ad%a5" class="header-anchor"&gt;&lt;/a&gt;实验 C：为何 API 必须“全同步或全异步”
&lt;/h3&gt;&lt;p&gt;官方反例：签名像异步、实际同步调用回调，调用方无法判断后续语句与回调的先后：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// BAD：可能同步也可能异步
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;maybeSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;node:fs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;file&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;推荐：同步路径也 &lt;code&gt;nextTick(cb)&lt;/code&gt;（或统一走 Promise），保证“总是异步”：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nextTick&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;node:process&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;node:fs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;definitelyAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;nextTick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;file&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;构造函数里 &lt;code&gt;emit&lt;/code&gt; 同理：应 &lt;code&gt;nextTick(() =&amp;gt; this.emit('event'))&lt;/code&gt;，让调用方先挂上监听器。&lt;/p&gt;
&lt;h3 id="实验-d同步计算堵死循环对照"&gt;&lt;a href="#%e5%ae%9e%e9%aa%8c-d%e5%90%8c%e6%ad%a5%e8%ae%a1%e7%ae%97%e5%a0%b5%e6%ad%bb%e5%be%aa%e7%8e%af%e5%af%b9%e7%85%a7" class="header-anchor"&gt;&lt;/a&gt;实验 D：同步计算堵死循环（对照）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// block-loop.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;node:http&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/block&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;// 同步忙等 3s
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;blocked\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ok\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;listen 3456&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;开两个终端：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 终端 1：触发阻塞&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sS http://127.0.0.1:3456/block &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 终端 2：立即请求轻量路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;time&lt;/span&gt; curl -sS http://127.0.0.1:3456/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会观察到轻量请求也被拖慢——因为 &lt;strong&gt;Event Loop 被同一线程的同步代码占满&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="四常见坑与排查"&gt;&lt;a href="#%e5%9b%9b%e5%b8%b8%e8%a7%81%e5%9d%91%e4%b8%8e%e6%8e%92%e6%9f%a5" class="header-anchor"&gt;&lt;/a&gt;四、常见坑与排查
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;现象&lt;/th&gt;
					&lt;th&gt;可能机制&lt;/th&gt;
					&lt;th&gt;处理方向&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;全站偶发卡顿、延迟尖刺&lt;/td&gt;
					&lt;td&gt;同步 CPU 重活 / 大 JSON / 灾难正则 / 长循环&lt;/td&gt;
					&lt;td&gt;限制输入；分区 yield；&lt;code&gt;worker_threads&lt;/code&gt; / 子进程卸载&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;setTimeout(100)&lt;/code&gt; 变成 105ms+&lt;/td&gt;
					&lt;td&gt;poll 长回调占住队列；阈值语义 + 其它回调&lt;/td&gt;
					&lt;td&gt;缩短 poll 回调；避免在 I/O 回调里做重计算&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;顶层 &lt;code&gt;setTimeout(0)&lt;/code&gt; 与 &lt;code&gt;setImmediate&lt;/code&gt; 顺序随机&lt;/td&gt;
					&lt;td&gt;主模块非 I/O 周期，时序依赖性能&lt;/td&gt;
					&lt;td&gt;需要稳定顺序时放到 I/O 回调，或只用一种调度 API&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;递归 &lt;code&gt;nextTick&lt;/code&gt; 后 I/O 永不完成&lt;/td&gt;
					&lt;td&gt;nextTick 队列饿死 poll&lt;/td&gt;
					&lt;td&gt;改 &lt;code&gt;setImmediate&lt;/code&gt; / 批处理 + yield&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CJS/ESM 下 Promise 与 nextTick 顺序不一致&lt;/td&gt;
					&lt;td&gt;官方文档写明的队列次序差异&lt;/td&gt;
					&lt;td&gt;不要依赖跨模块类型的微次序；业务用明确的 async 编排&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;fs&lt;/code&gt;/&lt;code&gt;crypto&lt;/code&gt; 变慢但 CPU 不高&lt;/td&gt;
					&lt;td&gt;Worker Pool 打满&lt;/td&gt;
					&lt;td&gt;评估 &lt;code&gt;UV_THREADPOOL_SIZE&lt;/code&gt;、减少同步 &lt;code&gt;*Sync&lt;/code&gt;、合并小 I/O&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;同步 API 伪装异步&lt;/td&gt;
					&lt;td&gt;调用方竞态&lt;/td&gt;
					&lt;td&gt;统一异步边界；错误路径也 &lt;code&gt;nextTick&lt;/code&gt;/&lt;code&gt;queueMicrotask&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;不要阻塞 Event Loop 的检查清单（摘自官方思路）：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;每个回调的计算复杂度是否随用户输入线性/超线性膨胀？是否有上限校验？&lt;/li&gt;
&lt;li&gt;是否存在嵌套量词正则（REDOS）？简单包含用 &lt;code&gt;indexOf&lt;/code&gt;/&lt;code&gt;includes&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;大对象是否在主线程 &lt;code&gt;JSON.parse&lt;/code&gt; / &lt;code&gt;stringify&lt;/code&gt;？能否限制体积或卸载到 Worker？&lt;/li&gt;
&lt;li&gt;是否误用 &lt;code&gt;*Sync&lt;/code&gt; 文件/加密 API 于请求路径？&lt;/li&gt;
&lt;li&gt;需要“下一轮再继续”时，默认优先 &lt;code&gt;setImmediate&lt;/code&gt; / &lt;code&gt;queueMicrotask&lt;/code&gt;，慎用无限 &lt;code&gt;nextTick&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;分区（Partitioning）示例思路&lt;/strong&gt;：把 O(n) 求和拆成多步，每步 O(1)，用 &lt;code&gt;setImmediate&lt;/code&gt;/&lt;code&gt;queueMicrotask&lt;/code&gt; 让出循环，避免单次回调霸占主线程。更重的任务则应 &lt;strong&gt;Offloading&lt;/strong&gt; 到 Worker（&lt;code&gt;worker_threads&lt;/code&gt;、独立进程或 C++/N-API 任务），而不是只靠在主循环里切片。&lt;/p&gt;
&lt;h2 id="五总结"&gt;&lt;a href="#%e4%ba%94%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;五、总结
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Node 的并发模型是 &lt;strong&gt;单线程 Event Loop + libuv Worker Pool&lt;/strong&gt;：I/O 与任务要“碎、短、可让出”。&lt;/li&gt;
&lt;li&gt;相位队列决定 &lt;strong&gt;timers / poll / check / close&lt;/strong&gt; 等回调归属；&lt;strong&gt;nextTick 与 microtask 是阶段之间的优先通道&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I/O 回调内&lt;/strong&gt; &lt;code&gt;setImmediate&lt;/code&gt; 稳定早于 &lt;code&gt;setTimeout(0)&lt;/code&gt;；主模块顶层两者顺序不稳定。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nextTick&lt;/code&gt; 适合“栈展开后、循环前进前”的 API 契约；滥用会饿死 I/O。新代码多数场景优先 &lt;strong&gt;&lt;code&gt;queueMicrotask&lt;/code&gt; / &lt;code&gt;setImmediate&lt;/code&gt;&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;性能与安全同一条线：限制输入、避免 REDOS、大 JSON 与 CPU 重活卸载，才能让“少线程高并发”真正成立。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把调度图画进团队约定后，再写中间件、ORM 钩子、连接池回调，会少掉一大类“玄学时序 bug”。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick" target="_blank" rel="noopener"
 &gt;The Node.js Event Loop&lt;/a&gt; — Node.js Learn（阶段图、poll/check/timers、&lt;code&gt;setImmediate&lt;/code&gt; vs &lt;code&gt;setTimeout&lt;/code&gt;、&lt;code&gt;process.nextTick&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://nodejs.org/en/learn/asynchronous-work/dont-block-the-event-loop" target="_blank" rel="noopener"
 &gt;Don&amp;rsquo;t Block the Event Loop (or the Worker Pool)&lt;/a&gt; — Node.js Learn（Event Loop vs Worker Pool、阻塞危害、分区/卸载、REDOS/JSON）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://nodejs.org/en/learn/asynchronous-work/understanding-processnexttick" target="_blank" rel="noopener"
 &gt;Understanding process.nextTick()&lt;/a&gt; — Node.js Learn&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://nodejs.org/api/process.html#processnexttickcallback-args" target="_blank" rel="noopener"
 &gt;&lt;code&gt;process.nextTick&lt;/code&gt; / &lt;code&gt;queueMicrotask&lt;/code&gt; 对比&lt;/a&gt; — Node.js API（CJS/ESM 次序、Legacy 说明）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://nodejs.org/api/timers.html" target="_blank" rel="noopener"
 &gt;Timers&lt;/a&gt; — Node.js API（&lt;code&gt;setImmediate&lt;/code&gt; / &lt;code&gt;setTimeout&lt;/code&gt;、ref/unref）&lt;/li&gt;
&lt;li&gt;源码文档镜像：&lt;a class="link" href="https://raw.githubusercontent.com/nodejs/node/main/doc/api/process.md" target="_blank" rel="noopener"
 &gt;nodejs/node &lt;code&gt;doc/api/process.md&lt;/code&gt;&lt;/a&gt;、&lt;a class="link" href="https://raw.githubusercontent.com/nodejs/node/main/doc/api/timers.md" target="_blank" rel="noopener"
 &gt;&lt;code&gt;doc/api/timers.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item></channel></rss>