Node.js のすべての非同期 I/O 操作は、完了時にイベントをイベント キューに送信します。

Node.js の多くのオブジェクトはイベントを送出します。net.Server オブジェクトは新しい接続が確立されるたびにイベントを送出し、fs.readStream オブジェクトはファイルが開かれるときにイベントを送出します。 これらのイベント発行オブジェクトはすべて events.EventEmitter のインスタンスです。

EventEmitter

events モジュールは、events.EventEmitter という 1 つのオブジェクトのみを提供します。 EventEmitter の中核は、イベント トリガー関数とイベント リスナー関数のカプセル化です。

このモジュールには、require("events"); によってアクセスできます。

// eventsモジュールの紹介
var events = require('events');
// eventEmitter オブジェクトを作成する。
var eventEmitter = new events.EventEmitter();

インスタンス化中にエラーが発生した場合、EventEmitter オブジェクトはエラー イベントを発行します。 newListener イベントは、新しいリスナーが追加されるときに発生し、removeListener イベントは、リスナーが削除されるときに発生します。

簡単な例を使用して、EventEmitter の使用法を説明してみましょう。

//event.js 
var EventEmitter = require('events').EventEmitter; 
var event = new EventEmitter(); 
event.on('some_event', function() { 
    console.log('some_event イベントトリガー'); 
}); 
setTimeout(function() { 
    event.emit('some_event'); 
}, 1000); 

実行結果は以下の通りです:

このコードを実行すると、1 秒後にコンソールに「some_event イベントがトリガーされました」 と出力されます。 原則として、イベント オブジェクトがイベント some_event のリスナーを登録し、1000 ミリ秒後に setTimeout を通じてイベント オブジェクトにイベント some_event を送信します。この時点で some_event のリスナーが呼び出されます。

$ node event.js 
some_event イベントトリガー

EventEmitter の各イベントは、イベント名といくつかのパラメーターで構成されており、イベント名は通常、特定のセマンティクスを表す文字列です。 イベントごとに、EventEmitter は複数のイベント リスナーをサポートします。

イベントがトリガーされると、このイベントに登録されているイベント リスナーが順番に呼び出され、イベント パラメータがコールバック関数のパラメータとして渡されます。

次の例でこのプロセスを説明しましょう。

//event.js 
var events = require('events'); 
var emitter = new events.EventEmitter(); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener1', arg1, arg2); 
}); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener2', arg1, arg2); 
}); 
emitter.emit('someEvent', 'arg1 パラメータ', 'arg2 パラメータ'); 

上記のコードを実行すると、以下のように実行されます。:

$ node event.js 
listener1 arg1 パラメータ arg2 パラメータ
listener2 arg1 パラメータ arg2 パラメータ

上の例では、エミッターはイベント someEvent に対して 2 つのイベント リスナーを登録し、someEvent イベントをトリガーします。

実行結果では、2 つのイベント リスナー コールバック関数が連続して呼び出されていることがわかります。 これは EventEmitter の最も単純な使用法です。

EventEmitter は、onemit などのいくつかの属性を提供します。 on 関数はイベント関数をバインドするために使用され、emit 属性はイベントをトリガーするために使用されます。 次に、EventEmitter のプロパティの紹介を詳しく見てみましょう。

方法

番号 メソッドと説明
1 addListener(イベント, リスナー)
指定されたイベントのリスナー配列の末尾にリスナーを追加します。
2 on(event,listener)
文字列イベントとコールバック関数を受け入れて、指定されたイベントのリスナーを登録します。
server.on('connection', function (stream) {
  console.log('someone connected!');
});
3 once(event, listener)
指定したイベントのワンタイム リスナーを登録します。つまり、リスナーは最大 1 回だけトリガーされ、トリガーされた後すぐに解放されます。
server.once('connection', function (stream) {
  console.log('Ah, we have our first user!');
});
4 removeListener(event, listener)

指定されたイベントのリスナーを削除します。リスナーはイベントに登録されたリスナーである必要があります。

これは 2 つのパラメータを受け入れます。1 つ目はイベント名、2 つ目はコールバック関数名です。

var callback = function(stream) {
  console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
5 removeAllListeners([event])
すべてのイベントのすべてのリスナーを削除します。event が指定されている場合は、指定されたイベント リスナーのすべてのリスナーが削除されます。
6 setMaxListeners(n)
デフォルトでは、10 を超えるリスナーを追加すると、EventEmitters は警告メッセージを出力します。 setMaxListeners 関数は、リスナーのデフォルトの制限数を変更するために使用されます。
7 listeners(event)
指定されたイベントのリスナーの配列を返します。
8 emit(event, [arg1], [arg2], [...])
次の場合に true を返す各リスナーを実行します。イベントには登録されたリスナーがあり、それ以外の場合は false を返します。

クラスメソッド

番号 メソッドと説明
1 listenerCount(emitter,event)
指定されたイベントのリスナーの数を返します。
events.EventEmitter.listenerCount(emitter,eventName) //非推奨、非推奨
events.emitter.listenerCount(eventName) //推奨

イベント

番号 イベントと説明
1 newListener
  • event - 文字列、イベント名

  • listener - イベント ハンドラー

このイベントは、新しいリスナーが追加されたときに発生します。

2 削除リスナー
  • event - 文字列、イベント名

  • listener - イベント ハンドラー

     

指定されたリスナー配列からリスナーを削除します。 この操作により、削除されたリスナーの後にそれらのリスナーのインデックスが変更されることに注意してください。

次の例は、connection (接続) イベントを介した EventEmitter クラスのアプリケーションを示しています。

main.js ファイルを作成します。コードは次のとおりです。

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

// リスナー #1
var listener1 = function listener1() {
  console.log('リスナーlistener1が実行されます。');
}

// リスナー #2
var listener2 = function listener2() {
 console.log('リスナーlistener2が実行されます。');
}

// 接続イベントをバインドします。ハンドラー関数はlistener1です
eventEmitter.addListener('connection', listener1);

// 接続イベントをバインドします。ハンドラー関数はlistener2です。
eventEmitter.on('connection', listener2);

var eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " 接続イベントをリッスンするリスナー。");

// connectionイベントを処理する
eventEmitter.emit('connection');

// モニターによってバインドされているlistener1関数を削除します
eventEmitter.removeListener('connection', listener1);
console.log("listener1 は聴取されなくなりました。");

// 接続イベントをトリガーする
eventEmitter.emit('connection');

eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " 接続イベントをリッスンするリスナー。");

console.log("プログラムの実行が完了しました");

上記のコードの実行結果は次のとおりです。

$ node main.js
2 つのリスナーが接続イベントをリッスンします。
リスナーlistener1が実行されます。
リスナーlistener2が実行されます。
listener1 は聴取されなくなりました。
リスナーlistener2が実行されます。
接続イベント用の 1 つのリスナー。
プログラムの実行が完了しました。

error イベント

EventEmitter は、間違ったセマンティクスを含む特殊なイベント errorを定義しています。 error イベントは通常、例外が発生したときにトリガーされます。

errorがトリガーされたとき、EventEmitter は応答がない場合に次のように指定します。 対応するリスナー Node.js はこれを例外として扱い、プログラムを終了し、error メッセージを出力します。

通常はerrorを発生させたいと考えています。 イベント オブジェクトは、エラー発生後にプログラム全体がクラッシュしないようにリスナーを設定します。 例:

var events = require('events'); 
var emitter = new events.EventEmitter(); 
emitter.emit('error'); 

を実行すると、以下のエラーが表示されます:

node.js:201 
throw e; // process.nextTick error, or 'error' event on first tick 
^ 
Error: Uncaught, unspecified 'error' event. 
at EventEmitter.emit (events.js:50:15) 
at Object.<anonymous> (/home/byvoid/error.js:5:9) 
at Module._compile (module.js:441:26) 
at Object..js (module.js:459:10) 
at Module.load (module.js:348:31) 
at Function._load (module.js:308:12) 
at Array.0 (module.js:479:10) 
at EventEmitter._tickCallback (node.js:192:40) 

EventEmitterの継承

ほとんどの場合、EventEmitter を直接使用するのではなく、オブジェクトの中で継承します。 これには、fs、net、および http が含まれ、イベント応答をサポートするすべてのコア モジュールは、EventEmitter のサブクラスである。

なぜこのようなことをするのでしょうか? 理由は2つあります:

第一に、あるエンティティの機能を持つオブジェクトがイベントを実装することは、意味的に一貫しています。 イベントのリスニングと発生は、そのオブジェクトのメソッドであるべきです。

第二に、JavaScriptのオブジェクト機構はプロトタイプベースであり、部分的な多重継承をサポートしています。 部分的な多重継承をサポートしており、EventEmitterを継承しても、オブジェクトの元の継承関係を破壊することはありません。