iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0
Modern Web

現在就學Node.js系列 第 8

事件驅動與 EventEmitter — Node.js 的事件世界 -Day8

  • 分享至 

  • xImage
  •  

Node.js 的核心設計理念就是 事件驅動(Event-driven)模型

這意味著程式並不是一行一行「執行到哪裡 → 等待結果 → 再繼續」,

而是依靠 事件觸發 來驅動程式邏輯。

什麼是事件驅動?

在事件驅動架構裡,事件 就是應用程式中的某個動作或狀態變化,例如:

  • 使用者送出一個 HTTP 請求
  • 檔案讀取完成
  • 計時器結束

每個事件都會對應到一個 處理邏輯(callback/handler)

一旦事件發生,程式就會執行相應的邏輯。

與傳統語言的差異

傳統同步流程(阻塞式)

讀檔 → 等待檔案完成 → 下一步

Node.js 非阻塞流程

發出讀檔任務 → 不等待 → 繼續執行其他程式
                     ↓
             檔案完成 → 觸發事件 → 執行 callback

這樣一來,單一執行緒 就能同時管理大量任務,因為主線程永遠不會被 I/O 卡住。

Node.js 的「事件驅動 + 非阻塞 I/O」特性,讓它非常擅長處理 高併發 I/O 密集型應用

  • API 伺服器 → 同時處理成千上萬個請求
  • 聊天室 → 即時訊息傳遞
  • 即時推播服務 → 快速廣播事件

因此,當 Node.js 發出任務(例如讀取檔案)時,程式本身並不會停下來等待結果。

相反地,它會先繼續執行其他邏輯,等到任務完成後,系統再透過 事件 通知程式回來處理。

換句話說,Node.js 把「等待 I/O 的時間」交給系統處理,主程式則持續運行其他工作,這種模式大幅提升了整體效能。

EventEmitter 是什麼?

在 Node.js 裡,幾乎所有能觸發事件的物件(HTTP server、檔案串流、socket 連線…)

都是基於 EventEmitter

它是 Node.js 提供的一個核心模組,負責管理 註冊監聽器觸發事件移除監聽器

import { EventEmitter } from "node:events";

const emitter = new EventEmitter();

emitter 就是一個事件中心,可以用來:

  • on/once → 註冊監聽器
  • emit → 觸發事件
  • off/removeListener → 移除監聽器

常用 API 教學

1. 註冊監聽器 — on()

emitter.on("ping", (msg) => console.log("收到:", msg));
emitter.emit("ping", "Hello World");

2. 只觸發一次 — once()

emitter.once("ready", () => console.log("只會觸發一次"));
emitter.emit("ready");
emitter.emit("ready"); // ❌ 不會再觸發

3. 觸發事件 — emit()

emitter.on("ping", (msg) => console.log("收到 ping:", msg));

emitter.emit("ping", "Hello Node.js");  // ✅
emitter.emit("pong", "沒人監聽");        // ❌

4. 移除監聽器 — off() / removeAllListeners()

const handler = (msg) => console.log("訊息:", msg);
emitter.on("message", handler);

emitter.off("message", handler);       // 移除指定
emitter.removeAllListeners("message"); // 移除某事件所有
emitter.removeAllListeners();          // 全部清空

⚠️ 注意:匿名函式無法移除,請先定義成具名函式。

5. 調整上限 — setMaxListeners(n)

emitter.setMaxListeners(20); // 預設 10

⚠️ 特殊事件:error

  • 觸發 error 卻沒有監聽器 → 程式會 直接 crash
emitter.on("error", (err) => console.error("錯誤:", err.message));
emitter.emit("error", new Error("爆炸啦 🚨"));

監聽器管理

方法 說明 範例
on(event, fn) 多次監聽 emitter.on("msg", fn)
once(event, fn) 只監聽一次 emitter.once("ready", fn)
emit(event, ...args) 觸發事件 emitter.emit("msg", "hi")
off(event, fn) 移除監聽器 emitter.off("msg", fn)
removeAllListeners() 移除所有 emitter.removeAllListeners()

emit 行為

狀況 行為
emit("ping") 有監聽器 立即同步執行
emit("pong") 無監聽器 無行為 靜悄悄
emit("error") 無監聽器 throw error → crash

範例:聊天室事件系統

我們來模擬一個 聊天室,用事件驅動來處理訊息。

import { EventEmitter } from "node:events";

const chat = new EventEmitter();

// 監聽訊息事件
chat.on("message", (user, msg) => {
  console.log(`💬 [${user}]:${msg}`);
});

// 模擬使用者發話
chat.emit("message", "Alice", "哈囉!");
chat.emit("message", "Bob", "嗨~歡迎加入聊天室!");

輸出:

💬 [Alice]:哈囉!
💬 [Bob]:嗨~歡迎加入聊天室!

👉 事件模式讓我們能輕鬆擴充:

例如增加「使用者上線」、「系統通知」、「斷線」事件,程式也不會變亂。

小結

  • Node.js 採用 事件驅動模型,核心工具是 EventEmitter
  • 學會 ononceemitoff/removeListenerremoveAllListeners 就能操作事件。
  • error 事件要特別注意,沒監聽會 crash。

參考資料:

Day 6: 事件驅動架構與 EventEmitter - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天

Node.js事件驅動模型 - 壹讀


上一篇
Node.js 之 fs 檔案系統 - Day7
系列文
現在就學Node.js8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言