# 通知設計書 12-EventEmitter

## 概要

本ドキュメントは、Node.jsの中核をなすイベント駆動プログラミング機構であるEventEmitterクラスの設計を記述する。EventEmitterは、オブジェクト間の疎結合な通信を可能にするPub/Subパターンを実装し、Node.jsのほぼ全てのI/Oモジュールの基盤となっている。

### 本通知の処理概要

EventEmitterは、特定のイベントに対してリスナー関数を登録し、そのイベントが発生（emit）した際に登録された全てのリスナーを同期的に呼び出す汎用的なイベント通知機構である。

**業務上の目的・背景**：Node.jsは非同期I/Oを効率的に処理するために設計されており、EventEmitterはこの非同期処理の結果を通知するための基盤メカニズムとして機能する。ファイルシステム操作、ネットワーク通信、ストリーム処理など、あらゆる非同期処理の完了・エラー・データ受信等のイベントをアプリケーションコードに通知する役割を担う。これにより開発者は、コールバック地獄を避けながら、イベント駆動型の直感的なプログラミングが可能となる。

**通知の送信タイミング**：`emit(eventName, ...args)`メソッドが呼び出された時点で即座に通知が送信される。これは完全に同期的な処理であり、全てのリスナーが順次実行される。また、リスナー登録時には`newListener`イベントが、削除時には`removeListener`イベントが自動的に発火する。

**通知の受信者**：`on()`、`addListener()`、`once()`、`prependListener()`、`prependOnceListener()`メソッドを使用して特定のイベント名に対してリスナー関数を登録した全てのコード。リスナーは登録順（または先頭挿入を指定した場合は逆順）に呼び出される。

**通知内容の概要**：emit()に渡されたイベント名と任意の引数がリスナー関数に渡される。引数の型や数に制限はなく、任意のJavaScript値を渡すことができる。

**期待されるアクション**：受信者（リスナー）は通知内容に基づき、データ処理、状態更新、後続処理の開始、エラーハンドリングなどを行う。特に`error`イベントは特別な扱いを受け、リスナーが登録されていない場合は例外がスローされる。

## 通知種別

内部イベント通知（EventEmitter）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（リスナーは登録順に同期実行） |
| 優先度 | 即時（emit呼び出し時点で実行） |
| リトライ | なし（リスナーでのエラーは呼び出し元に伝播） |

### 送信先決定ロジック

1. emit()でイベント名が指定される
2. `_events`オブジェクトから該当イベント名のリスナー配列を取得
3. 配列内のリスナーを登録順に呼び出し
4. onceで登録されたリスナーは初回呼び出し後に自動削除

## 通知テンプレート

### 特殊イベント

| イベント名 | 説明 | 発火タイミング |
|-----------|------|---------------|
| `newListener` | リスナー追加前に発火 | on/addListener/once等の呼び出し時 |
| `removeListener` | リスナー削除後に発火 | removeListener/off等の呼び出し時 |
| `error` | エラー発生時（特殊扱い） | 明示的なemit('error', err)呼び出し時 |

### 本文テンプレート

EventEmitterの通知ペイロードは自由形式であり、emit()の第2引数以降が全てリスナーに渡される：

```javascript
// 送信側
emitter.emit('data', chunk, encoding);

// 受信側
emitter.on('data', (chunk, encoding) => {
  // chunkとencodingを受け取る
});
```

### 添付ファイル

該当なし（メモリ内イベント通知のため）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| eventName | イベント名（string または symbol） | emit()の第1引数 | Yes |
| args | イベントに付随するデータ | emit()の第2引数以降 | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| メソッド呼び出し | emit(eventName, ...args) | リスナーが1つ以上登録されている | イベント発火 |
| メソッド呼び出し | on/addListener | 常に | newListenerイベントを事前発火 |
| メソッド呼び出し | removeListener/off | リスナーが存在 | removeListenerイベントを事後発火 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| リスナー未登録 | 該当イベントにリスナーがない場合はfalseを返して終了（errorイベントを除く） |
| _events未初期化 | EventEmitter.init()が未実行の場合 |

## 処理フロー

### 送信フロー（emit）

```mermaid
flowchart TD
    A[emit呼び出し] --> B{イベント名 == 'error'?}
    B -->|Yes| C{errorMonitorリスナーあり?}
    C -->|Yes| D[errorMonitorにemit]
    C -->|No| E{errorリスナーあり?}
    D --> E
    E -->|No| F[例外をスロー]
    E -->|Yes| G[リスナー呼び出し]
    B -->|No| H{リスナーあり?}
    H -->|No| I[false を返す]
    H -->|Yes| G
    G --> J{リスナーは関数?}
    J -->|Yes（単一）| K[ReflectApply で呼び出し]
    J -->|No（配列）| L[配列をクローンしてループ]
    K --> M{Promiseが返された?}
    L --> M
    M -->|Yes & captureRejections| N[addCatchでエラーハンドリング]
    M -->|No| O[true を返す]
    N --> O
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（メモリ内オブジェクトのみ使用）

### 内部データ構造

| プロパティ | 型 | 説明 |
|-----------|-----|------|
| _events | Object | イベント名をキー、リスナー（関数または配列）を値とするオブジェクト |
| _eventsCount | number | 登録されているイベント種別の数 |
| _maxListeners | number | 最大リスナー数（警告閾値） |

### 更新テーブル一覧

該当なし（メモリ内オブジェクトのみ使用）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| Unhandled 'error' event | errorイベントにリスナーがない状態でemit('error')が呼ばれた | ERR_UNHANDLED_ERRORがスロー |
| MaxListenersExceededWarning | 同一イベントにdefaultMaxListenersを超えるリスナーが登録された | process.emitWarning()で警告 |
| リスナーでの例外 | リスナー関数内で例外が発生 | 呼び出し元に伝播（他のリスナーは呼ばれない） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし（同期実行のため） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

### リスナー数制限

| 項目 | デフォルト値 |
|-----|-------------|
| defaultMaxListeners | 10 |
| setMaxListeners(n) | 任意の正の整数に変更可能 |

**注意**: 制限を超えてもリスナーの登録・実行は行われるが、メモリリークの可能性を示す警告が出力される。

### 配信時間帯

制限なし（同期実行のため即時）

## セキュリティ考慮事項

- リスナー関数はemit()の呼び出し元と同じコンテキストで実行されるため、信頼できないコードをリスナーとして登録しない
- errorイベントのハンドリングを忘れると、予期しない例外でプロセスがクラッシュする可能性がある
- once()で登録されたリスナーは自動削除されるため、タイミング攻撃に注意

## 備考

- EventEmitterはNode.jsの最も基本的なクラスの1つであり、stream、http、fs等の多くのモジュールが継承している
- captureRejectionsオプションを有効にすると、async関数リスナーからのPromise rejectionを自動的にerrorイベントとして処理できる
- EventEmitterAsyncResourceクラスを使用すると、async_hooksと統合したイベント発行が可能

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: EventEmitterの基本構造を理解する

EventEmitterの初期化と内部データ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | events.js | `lib/events.js` | EventEmitterコンストラクタとinit()メソッドを確認。_events, _eventsCount, _maxListenersの初期化 |

**読解のコツ**: `_events`オブジェクトは`{ __proto__: null }`で初期化され、プロトタイプチェーンを持たない純粋なマップとして機能する。これはパフォーマンスとセキュリティのため。

**主要処理フロー**:
1. **行208-210**: EventEmitterコンストラクタ - init()を呼び出すだけ
2. **行331-353**: init()メソッド - _events, _eventsCount, _maxListeners, kCaptureの初期化
3. **行255-257**: プロトタイプへの_events, _eventsCount, _maxListenersのデフォルト値設定

#### Step 2: emit()メソッドの実装を理解する

イベント発火のコアロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | events.js | `lib/events.js` | emit()メソッドの完全な実装。errorイベントの特殊処理、リスナー呼び出しロジック |

**主要処理フロー**:
1. **行455-456**: emit()の開始。type === 'error'かどうかの判定
2. **行458-464**: eventsオブジェクトの確認。errorMonitorへの通知
3. **行467-500**: errorイベントの特殊処理。リスナーがなければ例外スロー
4. **行502-505**: handlerの取得。undefinedならfalseを返す
5. **行507-530**: リスナーの呼び出し。単一関数と配列の両方に対応
6. **行513-515, 527-529**: captureRejectionsが有効な場合のPromise処理

#### Step 3: リスナー登録・削除の実装を理解する

on(), removeListener()等のリスナー管理ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | events.js | `lib/events.js` | _addListener()内部関数とremoveListener()メソッド |

**主要処理フロー**:
1. **行536-591**: _addListener()内部関数
   - **行541**: checkListener()でリスナーの型検証
   - **行548-557**: newListenerイベントの発火
   - **行561-575**: 単一リスナーの最適化と配列への変換
   - **行577-586**: MaxListenersExceededWarningの発行
2. **行599-601**: addListener() = on()
3. **行669-721**: removeListener() - リスナーの検索と削除、removeListenerイベントの発火

#### Step 4: once()とユーティリティ関数を理解する

一回限りのリスナーとPromiseベースのAPIを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | events.js | `lib/events.js` | once(), prependOnceListener(), 静的once()関数 |

**主要処理フロー**:
1. **行617-633**: _onceWrap() - onceリスナーのラッパー作成
2. **行641-646**: once()メソッド - _onceWrapを使用して登録
3. **行963-1005**: 静的once()関数 - Promiseベースのイベント待機

#### Step 5: AsyncIteratorとon()静的メソッドを理解する

イベントのAsyncIterableインターフェースを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | events.js | `lib/events.js` | 静的on()関数 - AsyncIteratorを返す |

**主要処理フロー**:
1. **行1047-1201**: 静的on()関数の実装
   - **行1063-1065**: unconsumedEvents/unconsumedPromisesキューの初期化
   - **行1070-1146**: iteratorオブジェクトの定義（next, return, throw）
   - **行1172-1180**: eventHandler - イベント受信とキュー処理

### プログラム呼び出し階層図

```
EventEmitter (lib/events.js)
    │
    ├─ constructor()
    │      └─ init()
    │             └─ _events = { __proto__: null }
    │
    ├─ emit(type, ...args)
    │      ├─ [type === 'error']
    │      │      ├─ emit(kErrorMonitor, ...args)
    │      │      └─ throw ERR_UNHANDLED_ERROR (if no listener)
    │      │
    │      ├─ handler = _events[type]
    │      │
    │      └─ [typeof handler === 'function']
    │             ├─ ReflectApply(handler, this, args)
    │             └─ addCatch() (if captureRejections)
    │
    ├─ on(type, listener) / addListener()
    │      └─ _addListener(this, type, listener, false)
    │             ├─ checkListener(listener)
    │             ├─ emit('newListener', type, listener)
    │             └─ _events[type] = listener or [existing, listener]
    │
    ├─ once(type, listener)
    │      └─ on(type, _onceWrap(this, type, listener))
    │             └─ onceWrapper()
    │                    └─ removeListener() + listener.call()
    │
    ├─ removeListener(type, listener) / off()
    │      ├─ [find listener in _events[type]]
    │      ├─ delete/splice from _events
    │      └─ emit('removeListener', type, listener)
    │
    └─ static once(emitter, name)
           └─ new Promise((resolve, reject) => ...)
                  ├─ emitter.once(name, resolver)
                  └─ emitter.once('error', errorListener)
```

### データフロー図

```
[イベント発行側]                 [EventEmitter]                    [イベント受信側]

emitter.emit('data', chunk)           │                        listener1(chunk)
        │                             │                              ↑
        │                             ▼                              │
        │                    ┌──────────────────┐                   │
        │                    │ _events オブジェクト │                   │
        │                    │ {                │                   │
        └──────────────────▶ │   data: [l1, l2] │ ─────────────────┘
                             │   error: l3      │                   │
                             │ }                │                   │
                             └──────────────────┘                   │
                                      │                              │
                                      ▼                        listener2(chunk)
                             ┌──────────────────┐                   ↑
                             │ ReflectApply     │                   │
                             │ (listener, this, │ ─────────────────┘
                             │  args)           │
                             └──────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| events.js | `lib/events.js` | ソース | EventEmitterの完全な実装。emit, on, removeListener等の全メソッド |
| internal/util.js | `lib/internal/util.js` | ソース | kEmptyObject, spliceOne等のユーティリティ関数 |
| internal/util/inspect.js | `lib/internal/util/inspect.js` | ソース | inspect関数（エラーメッセージ生成用） |
| internal/errors.js | `lib/internal/errors.js` | ソース | ERR_INVALID_ARG_TYPE, ERR_UNHANDLED_ERROR等のエラークラス |
| internal/validators.js | `lib/internal/validators.js` | ソース | validateFunction, validateNumber等のバリデーション関数 |
| internal/events/abort_listener.js | `lib/internal/events/abort_listener.js` | ソース | addAbortListener関数 |
| internal/fixed_queue.js | `lib/internal/fixed_queue.js` | ソース | AsyncIterator用のキュー実装 |
