Node.js 的核心設計理念就是 事件驅動(Event-driven)模型。
這意味著程式並不是一行一行「執行到哪裡 → 等待結果 → 再繼續」,
而是依靠 事件觸發 來驅動程式邏輯。
在事件驅動架構裡,事件 就是應用程式中的某個動作或狀態變化,例如:
每個事件都會對應到一個 處理邏輯(callback/handler),
一旦事件發生,程式就會執行相應的邏輯。
讀檔 → 等待檔案完成 → 下一步
發出讀檔任務 → 不等待 → 繼續執行其他程式
↓
檔案完成 → 觸發事件 → 執行 callback
這樣一來,單一執行緒 就能同時管理大量任務,因為主線程永遠不會被 I/O 卡住。
Node.js 的「事件驅動 + 非阻塞 I/O」特性,讓它非常擅長處理 高併發 I/O 密集型應用:
因此,當 Node.js 發出任務(例如讀取檔案)時,程式本身並不會停下來等待結果。
相反地,它會先繼續執行其他邏輯,等到任務完成後,系統再透過 事件 通知程式回來處理。
換句話說,Node.js 把「等待 I/O 的時間」交給系統處理,主程式則持續運行其他工作,這種模式大幅提升了整體效能。
在 Node.js 裡,幾乎所有能觸發事件的物件(HTTP server、檔案串流、socket 連線…)
都是基於 EventEmitter。
它是 Node.js 提供的一個核心模組,負責管理 註冊監聽器、觸發事件、移除監聽器。
import { EventEmitter } from "node:events";
const emitter = new EventEmitter();
emitter
就是一個事件中心,可以用來:
on/once
→ 註冊監聽器emit
→ 觸發事件off/removeListener
→ 移除監聽器on()
emitter.on("ping", (msg) => console.log("收到:", msg));
emitter.emit("ping", "Hello World");
once()
emitter.once("ready", () => console.log("只會觸發一次"));
emitter.emit("ready");
emitter.emit("ready"); // ❌ 不會再觸發
emit()
emitter.on("ping", (msg) => console.log("收到 ping:", msg));
emitter.emit("ping", "Hello Node.js"); // ✅
emitter.emit("pong", "沒人監聽"); // ❌
off()
/ removeAllListeners()
const handler = (msg) => console.log("訊息:", msg);
emitter.on("message", handler);
emitter.off("message", handler); // 移除指定
emitter.removeAllListeners("message"); // 移除某事件所有
emitter.removeAllListeners(); // 全部清空
⚠️ 注意:匿名函式無法移除,請先定義成具名函式。
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("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]:嗨~歡迎加入聊天室!
👉 事件模式讓我們能輕鬆擴充:
例如增加「使用者上線」、「系統通知」、「斷線」事件,程式也不會變亂。
on
、once
、emit
、off/removeListener
、removeAllListeners
就能操作事件。error
事件要特別注意,沒監聽會 crash。參考資料:
Day 6: 事件驅動架構與 EventEmitter - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天