<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>Node.js Event Loop｜林鼎淵 Dean Lin</title>
    <link>https://deanlin.net/tags/node.js-event-loop/</link>
    <image><url>https://deanlin.net/images/author_hu_aa256fc934e9a932.jpg</url><title>Node.js Event Loop｜林鼎淵 Dean Lin</title><link>https://deanlin.net/tags/node.js-event-loop/</link><width>144</width><height>144</height></image>
    <description>林鼎淵（Dean Lin）的部落格：生成式 AI 企業內訓講師、外商資安公司軟體專家、7 本書作者。400 多篇文章用白話分享生成式 AI 實戰、Claude Code 與 Vibe Coding 教學、團隊合作與職場成長。</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <copyright>© 2020–2026 林鼎淵 Dean Lin</copyright>
    <lastBuildDate>Tue, 10 Aug 2021 16:13:57 +0800</lastBuildDate>
    <atom:link href="https://deanlin.net/tags/node.js-event-loop/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Node.js 的 Event Loop (事件輪詢)到底在做什麼？</title>
      <link>https://deanlin.net/blog/202108/nodejs-event-loop/</link>
      <pubDate>Tue, 10 Aug 2021 16:13:57 +0800</pubDate>
      <guid>https://deanlin.net/blog/202108/nodejs-event-loop/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>技術教學</category>
      <description>因為網路上的教學文我大部分都有看沒有懂，只好自己實作一遍來了解他的運作機制；希望我這篇文章足夠口語化，讓讀者能輕鬆理解，如果有解釋錯誤或是不夠清晰的部分再煩請告知，謝謝。</description>
      <media:content url="https://deanlin.net/blog/202108/nodejs-event-loop/images/nodejs-event-loop-og_hu_37b29e95635eaae5.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p>因為網路上的教學文我大部分都有看沒有懂，只好自己實作一遍來了解他的運作機制；希望我這篇文章足夠口語化，讓讀者能輕鬆理解，如果有解釋錯誤或是不夠清晰的部分再煩請告知，謝謝。</p>
<p><img src="https://deanlin.net/blog/202108/nodejs-event-loop/images/nodejs-event-loop-01.webp" width="1400" height="788" alt="Node.js 的 Event Loop (事件輪詢)到底在做什麼？（圖 1）" fetchpriority="high" decoding="async"></p>
<pre tabindex="0"><code>大綱
一、請用一段話簡述 Node.js 的 Event Loop
二、Node.js 在 Event Loop 邏輯下的執行順序
三、Event Loop 執行的邏輯
  ➤ 了解 MacroTask 的 6 個階段
  ➤ NextTick Queue 優先層級最高
  ➤ MicroTask Queue 優先層級次高
四、解析一開始 Node.js 範例程式執行順序
  ➤ 順序執行，先把同步函式解決
  ➤ 進入 Event Loop
</code></pre><h2 id="一請用一段話簡述-nodejs-的-event-loop">一、請用一段話簡述 Node.js 的 Event Loop<a class="h-anchor" href="#%e4%b8%80%e8%ab%8b%e7%94%a8%e4%b8%80%e6%ae%b5%e8%a9%b1%e7%b0%a1%e8%bf%b0-nodejs-%e7%9a%84-event-loop" aria-label="這個段落的連結"></a></h2>
<p>「Node.js 之所以高效，是因為採取單執行緒與 Event Loop 的概念；將所有需要等待結果、請求外部資源的函式，全部丟到 Event Loop 中等待；而 Event Loop 的邏輯是 Node.js 底層用 C 語言撰寫的 libuv 庫來運行的。」</p>
<p>有興趣的朋友可以來 GitHub 上看 libuv 的程式：</p>
<ol>
<li>前往 libuv 的 <a href="https://github.com/libuv/libuv">GitHub</a></li>
<li>看運作的核心程式碼「<a href="https://github.com/libuv/libuv/blob/v1.x/src/unix/core.c">core.c</a>」</li>
<li>搜尋「uv_run」，直接看最原本的邏輯</li>
</ol>
<p><img src="https://deanlin.net/blog/202108/nodejs-event-loop/images/nodejs-event-loop-02.webp" width="868" height="832" alt="一、請用一段話簡述 Node.js 的 Event Loop（圖 2）" loading="lazy" decoding="async"></p>
<hr>
<h2 id="二nodejs-在-event-loop-邏輯下的執行順序">二、Node.js 在 Event Loop 邏輯下的執行順序<a class="h-anchor" href="#%e4%ba%8cnodejs-%e5%9c%a8-event-loop-%e9%82%8f%e8%bc%af%e4%b8%8b%e7%9a%84%e5%9f%b7%e8%a1%8c%e9%a0%86%e5%ba%8f" aria-label="這個段落的連結"></a></h2>
<p>大家可以先用大腦模擬看看下面程式的執行順序：</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">&#39;start&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>process.nextTick(<span style="color:#ff7b72">function</span> () {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;nextTick1&#39;</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>setTimeout(<span style="color:#ff7b72">function</span> () {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;setTimeout&#39;</span>);
</span></span><span style="display:flex;"><span>}, <span style="color:#a5d6ff">0</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">new</span> Promise(<span style="color:#ff7b72">function</span> (resolve, reject) {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;promise&#39;</span>);
</span></span><span style="display:flex;"><span>    resolve(<span style="color:#a5d6ff">&#39;resolve&#39;</span>);
</span></span><span style="display:flex;"><span>}).then(<span style="color:#ff7b72">function</span> (result) {
</span></span><span style="display:flex;"><span>    console.log(result);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#ff7b72">async</span> <span style="color:#ff7b72">function</span> () {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;async&#39;</span>);
</span></span><span style="display:flex;"><span>})();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>setImmediate(<span style="color:#ff7b72">function</span> () {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;setImmediate&#39;</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>process.nextTick(<span style="color:#ff7b72">function</span> () {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;nextTick2&#39;</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">&#39;end&#39;</span>);
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/a8e641d53e5e81d7831b596a2e47af72" rel="noopener">event_loop.js</a>（GitHub Gist）</p>
<p>如果答案跟下面結果一樣，那代表你很了解 Node.js 的 Event Loop 邏輯：</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>start
</span></span><span style="display:flex;"><span>promise
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">async</span>
</span></span><span style="display:flex;"><span>end
</span></span><span style="display:flex;"><span>nextTick1
</span></span><span style="display:flex;"><span>nextTick2
</span></span><span style="display:flex;"><span>resolve
</span></span><span style="display:flex;"><span>setTimeout
</span></span><span style="display:flex;"><span>setImmediate
</span></span></code></pre></div><hr>
<h2 id="三event-loop-執行的邏輯">三、Event Loop 執行的邏輯<a class="h-anchor" href="#%e4%b8%89event-loop-%e5%9f%b7%e8%a1%8c%e7%9a%84%e9%82%8f%e8%bc%af" aria-label="這個段落的連結"></a></h2>
<figure class="figure"><img src="https://deanlin.net/blog/202108/nodejs-event-loop/images/nodejs-event-loop-03.webp" width="1400" height="792" alt="Node.js 的 Event Loop" loading="lazy" decoding="async">
  <figcaption>Node.js 的 Event Loop</figcaption>
</figure>

<h3 id="-了解-macrotask-的-6-個階段">了解 MacroTask 的 6 個階段<a class="h-anchor" href="#-%e4%ba%86%e8%a7%a3-macrotask-%e7%9a%84-6-%e5%80%8b%e9%9a%8e%e6%ae%b5" aria-label="這個段落的連結"></a></h3>
<p><strong>Timer</strong>：等計時器（setTimeout、setInterval）的時間一到，會把他們的 callback 放在這裡等待執行。</p>
<p><strong>Pending callbacks</strong>：作業系統層級使用（TCP errors、sockets 連線被拒絕）。</p>
<p><strong>Idle, Prepare</strong>：內部使用。</p>
<p><strong>Polling</strong>：</p>
<ol>
<li>如果 Queue 不為空，依次取出 callback 函數執行，直到 Queue 為空或是抵達系統最大限制。</li>
<li>如果 Queue 為空但<em>有</em>設置「setImmediate」就進入 check 階段。</li>
<li>如果 Queue 為空但<em>沒有</em>設置「setImmediate」就會在 Polling 階段等待直到 Queue 有東西或是 Timer 時間抵達。</li>
</ol>
<p><strong>Check</strong>：處理 setImmediate 的 callback。</p>
<p><strong>Close callbacks</strong>：處理關閉連線、檔案的 callback。</p>
<blockquote>
<p>Macrotask Queue 在 Evevnt Loop 優先層級較低，接下來介紹優先層級更高的兩個 Queue。</p>
</blockquote>
<h3 id="-nexttick-queue-優先層級最高">NextTick Queue 優先層級最高<a class="h-anchor" href="#-nexttick-queue-%e5%84%aa%e5%85%88%e5%b1%a4%e7%b4%9a%e6%9c%80%e9%ab%98" aria-label="這個段落的連結"></a></h3>
<p>「process.nextTick()」的 callback 可以隨意插隊；只要這個 Queue 有東西就會優先執行。</p>
<h3 id="-microtask-queue-優先層級次高">MicroTask Queue 優先層級次高<a class="h-anchor" href="#-microtask-queue-%e5%84%aa%e5%85%88%e5%b1%a4%e7%b4%9a%e6%ac%a1%e9%ab%98" aria-label="這個段落的連結"></a></h3>
<p>「Promise 」的狀態從 pending 轉變為 resolve 或 reject 時，此時執行的 callback 會進入這個 Queue。</p>
<blockquote>
<p>注意：Promise 在創建時是同步的，不會進入 Event Loop。</p>
</blockquote>
<hr>
<h2 id="四解析一開始-nodejs-範例程式執行順序">四、解析一開始 Node.js 範例程式執行順序<a class="h-anchor" href="#%e5%9b%9b%e8%a7%a3%e6%9e%90%e4%b8%80%e9%96%8b%e5%a7%8b-nodejs-%e7%af%84%e4%be%8b%e7%a8%8b%e5%bc%8f%e5%9f%b7%e8%a1%8c%e9%a0%86%e5%ba%8f" aria-label="這個段落的連結"></a></h2>
<h3 id="-順序執行先把同步函式解決">順序執行，先把同步函式解決<a class="h-anchor" href="#-%e9%a0%86%e5%ba%8f%e5%9f%b7%e8%a1%8c%e5%85%88%e6%8a%8a%e5%90%8c%e6%ad%a5%e5%87%bd%e5%bc%8f%e8%a7%a3%e6%b1%ba" aria-label="這個段落的連結"></a></h3>
<ol>
<li>因為 Promise 在創建時是同步的，所以會先執行</li>
<li>在 function 前面加上 async 宣告為異步函式，執行時會被轉換為 Promise</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">&#39;start&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">new</span> Promise(<span style="color:#ff7b72">function</span> (resolve, reject) {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;promise&#39;</span>);
</span></span><span style="display:flex;"><span>    resolve(<span style="color:#a5d6ff">&#39;resolve&#39;</span>);
</span></span><span style="display:flex;"><span>}).then(<span style="color:#ff7b72">function</span> (result) {
</span></span><span style="display:flex;"><span>    console.log(result);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#ff7b72">async</span> <span style="color:#ff7b72">function</span> () {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;async&#39;</span>);
</span></span><span style="display:flex;"><span>})();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">&#39;end&#39;</span>);
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/44406b76d559e2183b9514e290da2f08" rel="noopener">sync_function.js</a>（GitHub Gist）</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>start
</span></span><span style="display:flex;"><span>promise
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">async</span>
</span></span><span style="display:flex;"><span>end
</span></span></code></pre></div><h3 id="-進入-event-loop">進入 Event Loop<a class="h-anchor" href="#-%e9%80%b2%e5%85%a5-event-loop" aria-label="這個段落的連結"></a></h3>
<ol>
<li>如果執行過程中遇到 NextTick Queue 就優先執行</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>process.nextTick(<span style="color:#ff7b72">function</span> () {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;nextTick1&#39;</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>process.nextTick(<span style="color:#ff7b72">function</span> () {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;nextTick2&#39;</span>);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/5eaffaea4c9d482ffb36a111fbf1fd2f" rel="noopener">nextTick_queue.js</a>（GitHub Gist）</p>
<pre tabindex="0"><code>nextTick1
nextTick2
</code></pre><p>2. 在處理完最高優先層級後，如果執行過程中遇到 MicroTask Queue 就執行</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">new</span> Promise(<span style="color:#ff7b72">function</span> (resolve, reject) {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;promise&#39;</span>);
</span></span><span style="display:flex;"><span>    resolve(<span style="color:#a5d6ff">&#39;resolve&#39;</span>);
</span></span><span style="display:flex;"><span>}).then(<span style="color:#ff7b72">function</span> (result) {
</span></span><span style="display:flex;"><span>    console.log(result);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/d5a22e3600e7d0334cb213051bc8ca40" rel="noopener">MicroTask_queue.js</a>（GitHub Gist）</p>
<pre tabindex="0"><code>resolve
</code></pre><p>3. 在前兩個高優先層級的 Queue 清空後，返回 Event Loop 繼續執行，setTimeout 設定的時間已到先執行。</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>setTimeout(<span style="color:#ff7b72">function</span> () {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;setTimeout&#39;</span>);
</span></span><span style="display:flex;"><span>}, <span style="color:#a5d6ff">0</span>);
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/cdcdddce3fd03f545e6f159956cf9c7f" rel="noopener">setTimeout_function.js</a>（GitHub Gist）</p>
<pre tabindex="0"><code>setTimeout
</code></pre><p>4. 接著再在「Check」 階段執行 setImmediate</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>setImmediate(<span style="color:#ff7b72">function</span> () {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">&#39;setImmediate&#39;</span>);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/5cba974b22f28334f0b21e268081e3dc" rel="noopener">setImmediate_function.js</a>（GitHub Gist）</p>
<pre tabindex="0"><code>setImmediate
</code></pre><p>以上步驟執行完成後就是範例程式的解答啦～希望對大家理解 Event Loop 有幫助。</p>
<p><strong>參考資源：</strong></p>
<ol>
<li><a href="https://notes.andywu.tw/2020/%E5%AE%8C%E6%95%B4%E5%9C%96%E8%A7%A3node-js%E7%9A%84event-loop%E4%BA%8B%E4%BB%B6%E8%BF%B4%E5%9C%88/">完整圖解Node.js的Event Loop(事件迴圈)</a></li>
<li><a href="https://www.youtube.com/watch?v=yHnlOAKYxQU&amp;ab_channel=%E6%9D%8E%E7%9A%93%E8%AF%9A">15 尚硅谷 前端面试题 nodejs事件轮询机制</a></li>
</ol>
]]></content:encoded>
    </item>
  </channel>
</rss>
