NodeJS 速習チュートリアル

Node.js Events

1. Node.js におけるイベントの核心概念

コンピューター上のあらゆるアクションは「イベント」です。例えば、ネットワーク接続が確立されたときや、ファイルがオープンされたときなどがこれに該当します。

Node.js のオブジェクトはイベントを発生(ファイア)させることができます。例えば、readStream オブジェクトは、ファイルを開いたり閉じたりしたときにイベントを発行します。

例:

let fs = require('fs');
// ファイルの読み込みストリームを作成
let rs = fs.createReadStream('./demofile.txt');

// 'open' イベントを監視
rs.on('open', function () {
  console.log('ファイルがオープンされました');
});

2. Node.js Events のはじめ方

Node.js は イベント駆動(Event-driven) アーキテクチャを採用しています。ここでは、「エミッター(Emitters)」と呼ばれるオブジェクトが名前付きのイベントを発行し、それが「リスナー(Listeners)」と呼ばれる関数オブジェクトの呼び出しを引き起こします。

2.1 基本的な例

// events モジュールをインポート
const EventEmitter = require('events');

// EventEmitter のインスタンスを作成
const myEmitter = new EventEmitter();

// イベントリスナーを登録
myEmitter.on('greet', () => {
  console.log('こんにちは!');
});

// イベントを発行
myEmitter.emit('greet'); // 出力: こんにちは!

3. EventEmitter クラス

EventEmitter クラスは、Node.js のイベント駆動アーキテクチャの根幹をなすものです。これを利用することで、独自のカスタムイベントを作成し、ハンドルする機能を実装できます。

3.1 イベントエミッターの作成

EventEmitter を使用するには、まずそのインスタンスを作成する必要があります。

let events = require('events');
let eventEmitter = new events.EventEmitter();

4. EventEmitter オブジェクト

EventEmitter オブジェクトを使用すると、独自のイベントにイベントハンドラを割り当てることができます。

以下の例では、"scream"(叫び)イベントがファイアされたときに実行される関数を作成しています。イベントをファイアさせるには、emit() メソッドを使用します。

例:

let events = require('events');
let eventEmitter = new events.EventEmitter();

// イベントハンドラ(関数)を作成
let myEventHandler = function () {
  console.log('叫び声が聞こえました!');
}

// イベントにハンドラを割り当て(登録)
eventEmitter.on('scream', myEventHandler);

// 'scream' イベントをファイア
eventEmitter.emit('scream');

5. よく使われる EventEmitter パターン

5.1 イベントハンドラへの引数の受け渡し

イベント発行時にデータを渡すことができます。

例:

const EventEmitter = require('events');
const emitter = new EventEmitter();

// 引数を受け取るリスナーを登録
emitter.on('userJoined', (username, userId) => {
  console.log(`${username} (${userId}) がチャットに参加しました`);
});

// 引数を指定してイベントを発行
emitter.emit('userJoined', 'JohnDoe', 42);
// 出力: JohnDoe (42) がチャットに参加しました

5.2 1回だけ実行されるイベントハンドラ

once() メソッドを使用すると、最初の1回だけ実行され、その後は自動的に削除されるリスナーを登録できます。

例:

const EventEmitter = require('events');
const emitter = new EventEmitter();

// このリスナーは一度だけ呼び出されます
emitter.once('connection', () => {
  console.log('初回接続が確立されました');
});

emitter.emit('connection'); // リスナーが実行される
emitter.emit('connection'); // 2回目は実行されない

5.3 エラーハンドリング

Node.js において、error イベントは特別な意味を持ちます。

例:

const EventEmitter = require('events');
const emitter = new EventEmitter();

// 'error' イベントは常にハンドルするようにしましょう
emitter.on('error', (err) => {
  console.error('エラーが発生しました:', err.message);
});

// エラーイベントを発行
emitter.emit('error', new Error('何らかの不具合が発生しました'));

6. ベストプラクティス

6.1 必ずエラーをハンドルする

エラーイベントに対するリスナーがない場合、Node.js プロセス全体がクラッシュする可能性があります。

// Good practice: 常に 'error' イベントをリッスンする
myEmitter.on('error', (err) => {
  console.error('EventEmitter 内でエラーが発生:', err);
});

6.2 スタックトレースのために名前付き関数を使用する

匿名関数(アロー関数など)ではなく名前付き関数を使用すると、デバッグ時のスタックトレースが追いやすくなります。

// 匿名関数ではなく、名前付き関数を定義
function handleData(data) {
  console.log('データを受信:', data);
}

myEmitter.on('data', handleData);

6.3 リスナーのクリーンアップ

不要になったリスナーは削除することで、メモリリークを防ぐことができます。

// リスナーを追加
const listener = () => console.log('イベントが発生しました');
myEmitter.on('event', listener);

// 不要になったタイミングでリスナーを削除
myEmitter.off('event', listener);