概要
JavaScript(特にNode.js)において、EventEmitter
はカスタムイベントを中心にした通信インターフェースとして利用される。
これにより、モジュールやレイヤー間の疎結合な双方向通信が可能となり、UIコンポーネントの再利用性、非同期処理の柔軟性、責務の明確な分離を実現できる。
本稿では以下を扱う:
-
EventEmitter
の基本APIと構造 - イベント購読・発火・解除の流れ
- クラスベースでのカスタム実装
- DOMのイベントと明確に分けた抽象化の意義
- アーキテクチャ設計上の使い所と注意点
Node.jsのEventEmitter基本構文
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('login', user => {
console.log(`${user} logged in`);
});
emitter.emit('login', 'toto'); // "toto logged in"
- ✅
on(event, listener)
→ イベント購読 - ✅
emit(event, data)
→ イベント発火 - ✅
off(event, listener)
→ イベント解除(Node 10以降)
onceで一度きりのリスナー設計
emitter.once('init', () => {
console.log('Initialized');
});
→ ✅ 初回のみ有効 → 初期化処理、初回ロード検出などに有効
リスナーの解除
function handler() {
console.log('Logout event');
}
emitter.on('logout', handler);
emitter.off('logout', handler);
→ ✅ 明示的な解除でメモリリーク防止
イベントを内包するクラス構造(カスタムラッパー)
class AuthService extends EventEmitter {
login(user) {
// ログイン処理...
this.emit('login', user);
}
logout() {
this.emit('logout');
}
}
const auth = new AuthService();
auth.on('login', u => console.log(`${u} is online`));
auth.login('toto');
→ ✅ イベントの設計単位をクラスの責務に内包
DOMイベントとの明確な違い
項目 | EventEmitter | DOMイベント |
---|---|---|
発火元 | 任意のオブジェクト | DOMノード |
イベント名 | 自由な文字列 | 固定されたDOMイベント(click等) |
バブリング/キャプチャ | ❌ なし | ✅ あり |
ライフサイクル制御 | ✅ メソッドで明示的に管理 | ❌ 暗黙的 |
イベント駆動設計の責務分離パターン
[UI Layer] ── on('submit') → [FormService] ── emit('submitted') → [Logger/Analytics]
- ✅ 複数のリスナーが独立に反応
- ✅ UI・処理・ログなどの責務を「イベント」で中継可能
- ✅ テスト容易・柔軟な構成変更が可能
イベントバス(event bus)としての活用
const EventBus = new EventEmitter();
// グローバル通信ハブとしてUI間を接続
EventBus.on('notify', msg => alert(msg));
EventBus.emit('notify', '登録に成功しました');
→ ✅ シンプルなグローバルメッセージング構造
よくある注意点
❌ メモリリーク(過剰なリスナー)
emitter.setMaxListeners(10); // 超えると警告
→ ✅ removeListener
, off
, once
で制御必須
❌ 同一イベント名の曖昧さ
→ ✅ イベント名は明示的にドメイン命名:user:login
, system:shutdown
など
設計判断フロー
① オブジェクト間を疎結合でつなぎたい? → EventEmitter
② 特定の処理後に任意数のリスナーを通知したい? → emitパターン
③ モジュールごとにイベント定義したい? → クラス継承 + EventEmitter
④ グローバル通信が欲しい? → EventBus設計
⑤ イベント数が多い? → イベント名を明示的に命名(名前空間風)
結語
EventEmitter
は「状態に反応するという設計思想」を、構文として提供する。
それは単なるAPIではなく、「責務の通知可能な抽象化」である。
- 情報の流れを疎結合に制御し、
- 処理の分岐を設計的に明示し、
- 再利用可能でテストしやすい構造を作る
関数を呼ぶのではなく、反応させる。
それがイベント駆動設計の本質であり、EventEmitter
の力である。