Node.js API (events) - イベントシステム

Node.js の API の多くは非同期のイベント駆動構造で作成されてます。 その機能は標準モジュール"events" が提供しています。
今回は events モジュールのイベント駆動のシステムについて解説します。

イベント駆動の基本

Node.js では「 emitter 」(エミッター、発行者) と呼ばれるオブジェクトが用意されており、 これに イベントに関連付けて関数を登録しておくと、イベントが発生した時にその関数が実行されます。 一般的にはこの関数はコールバック関数と呼ばれますが、 Node.js では 「 listener 」(リスナー、傾聴者) と呼んでいます。

例えば、 net.Socket オブジェクトではデータを受信すると "data" というイベントを発行します。 そのデータの受信処理の関数を登録して、受信時に呼ばれるようにします。


エミッターは EventEmitter クラスのインスタンスです。 自分でエミッターを作りたい場合は EventEmitter を継承して作成します。
以下のサンプルでは ES6 の継承機能を使ってますが、 Node.js では util.inherits という関数も用意されています。
"use strict";

const EventEmitter = require('events');

// 継承して emitter を作成
class MyEmitter extends EventEmitter {};
var myEmitter = new MyEmitter();
リスナーの登録は EventEmitter の on メソッドにイベント名とリスナーを渡します。 イベント名はキャメルケースでつけるのが慣例です。
// listener の登録
function barListener()
{
  console.log('a foo event occurred!');  
}
myEmitter.on('fooEvent', barListener);
イベントを発行する(イベントを発生させる)には イベント名を引数として emit メソッドを実行します。
// イベントの発行
myEmitter.emit('fooEvent');
// barListener の実行 => a foo event occurred!
リスナーを実行させようとすることを call (呼び出し) ではなく、 invoke (呼びかけ) といいます。
上記は on() メソッドで登録しましたが、 once() でも登録することができます。ただ、この場合は何度イベントを発行しても、リスナーは最初の一度しか実行されません。また、後述するように非同期で実行させることもあります。
このようにリスナーは状況により実行されなかったり、後回しになったりするので、 「呼びかけ」であり、コールバックではなく「リスナー」なのではないかと思います。

リスナー

引数

イベント発生時の情報を処理側であるリスナーに渡したい場合には引数を使います。
var myEmitter = new MyEmitter();

myEmitter.on('fooEvent', function(a, b) {
    console.log('fooEvent', a, b);
});
myEmitter.emit('fooEvent', 'bar', 'baz');
// fooEvent bar baz

this

JavaScript のややこしいところに this は何を指しているかという問題があります。
リスナー実行時の this はイベントを発行したオブジェクトに書き換えられています。
var myEmitter = new MyEmitter();

myEmitter.on('fooEvent', function() {
    console.log('fooEvent this =', this);
});
myEmitter.emit('fooEvent');
// fooEvent this= MyEmitter {
//   domain: null,
//   _events: { fooEvent: [Function] },
//   _eventsCount: 1,
//   _maxListeners: undefined }
ES6 のアロー関数を使うと this は守られます。
var myEmitter = new MyEmitter();

myEmitter.on('fooEvent', () => {
    console.log('fooEvent this = ', this);
});

console.log('this = ', this);   // this = {}
myEmitter.emit('fooEvent');     // fooEvent this = {}

同期と非同期

一つのイベントに対して複数のリスナーを登録することができ、 リスナーはイベントに登録した順に実行されます。
リスナーの実行はイベントが発行されるとすぐに行われる同期処理です。
var myEmitter = new MyEmitter();

myEmitter.on('fooEvent', () => console.log('1'));
myEmitter.on('fooEvent', () => console.log('2'));
myEmitter.on('fooEvent', () => console.log('3'));

myEmitter.emit('fooEvent');
// 1
// 2
// 3
イベントの発生と処理の実行を非同期にしたい場合は setImmediate()process.nextTick() を使います。
var myEmitter = new MyEmitter();

myEmitter.on('fooEvent', () => console.log('1'));
myEmitter.on('fooEvent', () => {
    setImmediate(() => console.log('2 (async)'));
});
myEmitter.on('fooEvent', () => console.log('3'));

myEmitter.emit('fooEvent');
// 1
// 3
// 2 (async)
非同期にするための関数が setImmediate(Imediate : 直接の、即時の)というのはちょっと変に感じるかもしれませんが、 これは setTimeout の待ち時間 0 という意味での Immediate です。 また setImmediate() と process.nextTick() は微妙に違うらしいです。

エラーイベント

用意するイベントとそのイベント名はエミッターを作る側で自由に付けられます。 ただ、エラーが発生した場合には "error" イベントを発生させ、エラーオブジェクトを渡すのが慣例になっています。

このエラーイベントが発生し、何もしていないと Node.js はエラーで落ちます。
var myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
// events.js:141
//       throw er; // Unhandled 'error' event
//       ^
// Error: whoops!
//     at Object.<anonymous> (d:h...
エミッターを使う側は "error" イベントにはエラー処理のリスナーを必ず登録するのが、 ベストプラクティスとなっています。
var myEmitter = new MyEmitter();
myEmitter.on('error', (err) => {
    console.log('myEmitter error happned. ', err);
});
myEmitter.emit('error', new Error('whoops!'));
// myEmitter error happned.  [Error: whoops!]

サンプルコード

記事で使用したサンプルコードは以下のリンクから取得(名前をつけて保存)できます。 これを node コマンドの引数に渡して実行します。
$ node events.js 
node コマンドの使い方について詳しくは以前の記事をご覧ください。

関連記事
スポンサーサイト
Prev.    Category    Next 

Facebook コメント


コメント

コメントの投稿

Font & Icon
非公開コメント

このページをシェア
アクセスカウンター
アクセスランキング
[ジャンルランキング]
コンピュータ
39位
アクセスランキングを見る>>

[サブジャンルランキング]
プログラミング
4位
アクセスランキングを見る>>
カレンダー(アーカイブ)
プルダウン 降順 昇順 年別

04月 | 2017年05月 | 06月
- 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 - - -


はてな新着記事
はてな人気記事
ブロとも申請フォーム
プロフィール

yohshiy

Author:yohshiy
職業プログラマー。
仕事は主に C++ ですが、軽い言語マニアなので、色々使っています。

はてブ:yohshiy のブックマーク
Twitter:@yohshiy

サイト紹介
プログラミング好きのブログです。プログラミング関連の話題や公開ソフトの開発記などを雑多に書いてます。ただ、たまに英語やネット系の話になることも。