Node.jsのEventEmitterについて書いていきます。
EventEmitterとは
Node.jsにおいて独自のイベントを作成したいときに使われます。
EventEmitterの基本的な使い方は、onやonceメソッドなどを使って、イベント名を登録して、リスナーに登録したイベントが呼び出されたときに実行する処理を書いていきます。ここでいうイベントの呼び出しとは、emitメソッドでイベント名を指定して登録したイベントを呼び出します。
'start'イベントと'end'イベントを実装した簡単な例をやってみたいと思います:
const { EventEmitter } = require("events");
const eventEmitter = new EventEmitter();
eventEmitter.on('start', () => {
console.log('start event!');
});
// This method don't run.
eventEmitter.on('end', () => {
console.log('end event!');
});
eventEmitter.emit('start');
これを実行すると'start'イベントのみをemitしているので、'start'イベントは実行され、'end'イベントは実行されません。
onメソッドは第一引数にイベント名、第二引数にはリスナーをコールバック関数で実装します。
emitメソッドは指定したイベント名が登録されていれば、イベントを呼び出して、リスナーを実行します。第一引数にはイベント名が入ります。その他にも引数が存在しますがそれはこの後に説明したいと思います。
リスナーに引数を渡す
EventEmitterでリスナーに対して引数を渡したいときは、まずイベントを登録するメソッドのリスナーの実装部分で引数を設定します。そのあとに、emitメソッドで第二引数以降に登録したイベントに対応した引数を入れていくとリスナーに引数を渡すことができます。
実際に実装してみるとこんな感じです:
eventEmitter.on('setting', (obj, version) => {
console.log('name: ' + obj.name + ' message: ' + obj.message);
console.log('This version is ' + version);
});
eventEmitter.emit('setting', {name: 'event setting', message: 'Hello!!'}, '1.3.4');
// Print
// name: event setting message: Hello!!
// This version is 1.3.4
そのほかのメソッド
ここではonとemitメソッド以外で気になったものを書いていきたいと思います。
onceメソッド
最初の説明でonceメソッドというのがありましたが、このメソッドは基本的にはonメソッドと変わりませんが、このメソッドで登録したイベントは一度しか呼び出されないということです。
実際にさっきのコードのonメソッドのどれかをonceメソッドに書き換えた後に、同じイベントをemitメソッドで複数回呼び出してみてください。onceメソッドで登録したイベントは一度しか実行されていないと思います、
eventEmitter.once('setting', (obj, version) => {
console.log('name: ' + obj.name + ' message: ' + obj.message);
console.log('This version is ' + version);
});
eventEmitter.emit('setting', {name: 'event setting', message: 'Hello!!'}, '1.3.4');
eventEmitter.emit('setting', {name: 'event setting', message: 'Hello!!'}, '1.3.4');
リスナーの追加と削除
リスナーの追加と削除はaddListenerとremoveListenerメソッドでできます。
リスナーに追加したいときは、addListenerメソッドの第一引数にリスナーを追加したいイベント名、第二引数に実際に追加するリスナーを実装します。
リスナーを削除したいときは、removeListenerメソッドの第一引数に削除したいリスナーのあるイベント名、第二引数には削除したいリスナーを入れます。注意してほしいのがこの第二引数には無名関数ではなく、リスナーの関数名を入れること。
例えば、以下の図のように'box'イベントにリスナーを追加する'addItem1'と'addItem2'イベントを作成して、'addItem1'イベントで追加したリスナーを'removeItem'イベントで削除するイベントを作成します。
コードにするとこんな感じです:
const { EventEmitter } = require("events");
const eventEmitter = new EventEmitter();
eventEmitter.on('box', () => {
console.log('listener1');
console.log('Sum of this listener is ' + eventEmitter.listenerCount('box'));
});;
const listenerMessage1 = () => console.log('This is listener1');
const listenerMessage2 = () => console.log('This is listener2');
eventEmitter.on('addItem1', () => {
console.log('add listener');
eventEmitter.addListener('box', listenerMessage1);
});
eventEmitter.on('addItem2', () => {
console.log('add listener');
eventEmitter.addListener('box', listenerMessage2);
});
eventEmitter.on('removeItem', () => {
console.log('remove listener');
eventEmitter.removeListener('box', listenerMessage1);
});
eventEmitter.emit('addItem1');
eventEmitter.emit('addItem2');
eventEmitter.emit('removeItem');
eventEmitter.emit('box');
ついでに、1つのイベントに登録できるリスナーの数はデフォルトでは10個です。この登録できるリスナーの数を変更するにはsetMaxListenersで設定できます。
リスナーを削除するメソッドは他にもあり、すべてのリスナーを削除する(イベント名を指定するとそのイベント内のリスナーすべてを削除する)メソッドであるremoveAllListenerやremoveListenerメソッドのエイアリスであるoffメソッドが使えます。
まえにあったonceメソッドではイベントが呼び出されたあとにこのメソッドで登録したリスナーは削除されます。
呼び出す前に呼び出す
特定のイベントの前にリスナーを呼び出したいときにprependListenerもしくはprependOnceListenerメソッドで登録できます。二つのメソッドの違いはonceが付いている方が一度だけ呼ばれるということです。
特別なイベント
EventEmitterには特別なイベントが存在します。リスナーが登録されたときに呼び出されるnewListener、リスナーが削除されたときに呼び出されるremoveListenerがあります。
const { EventEmitter } = require("events");
const eventEmitter = new EventEmitter();
var count = 0;
eventEmitter.on('newListener', () => {
++count;
console.log('Number of Listener is ' + count);
});
eventEmitter.on('removeListener', () => {
--count;
console.log('RemoveListener!');
console.log('Number of Listener after remeved is ' + count);
});
eventEmitter.on('start', () => {
console.log('Start event!');
});
const event1Callback = () => console.log('Add to event1');
eventEmitter.on('e1', event1Callback);
// This method don't run.
eventEmitter.on('end', () => {
console.log('End event!');
});
eventEmitter.emit('start');
eventEmitter.removeListener('e1', event1Callback);
実行すると
Number of Listener is 1
Number of Listener is 2
Number of Listener is 3
Number of Listener is 4
Start event!
RemoveListener!
となります。
実際に、onメソッドでリスナーが追加されるたびに、newListenerイベントが実行されています。そして、リスナーが削除されたときにはremoveListenerイベントが実行されていることがわかります。
さいご
実験的なものですが、captureRejectionsについて調べたかったのですがあまり情報が得られなかったので、いったん保留にしてます。機会があったらここに追記しておきます。
あと、いろいろ調べてみて、Node.jsのEventEmitterがなかなかに不評で、EventEmitter2や3とかがあったりしてて面白かったです。自分が最近ハマっているPuppeteerではバージョンが4.0.0になって、EventEmitterをなくしてMittというライブラリにしていこうという動きがあったりとか...
