はじめてのNode.js:Node.jsのイベントシステムを知る 3ページ

Node.jsのイベントループ機構——EventEmitterクラス

 続いて、非同期的なメソッドを持つクラスが利用する「イベント」という仕組みについて紹介しておこう。たとえばWebサーバーとしての処理が実装された「http.Server」というクラスでは、「待ち受けを行う」という処理に対し「クライアントからのリクエストを受信した」や「接続エラーが発生した」、「特別に処理すべきリクエストを受信した」といったさまざまなイベントが発生し、それぞれのイベントごとに異なる処理が必要となる。そこで、非同期的なメソッドを持つクラスではそのインスタンスに「イベントハンドラ」と呼ばれるコールバック関数を登録することで、それぞれのイベントに対応する処理を実行できるようになっている。

「イベントハンドラ」という用語について

 英語で記述されたドキュメントにおいては「listener」と呼ばれることもあるが、ここでは同様の意味を持ち日本語で広く使われている「イベントハンドラ」という用語を使用している

 たとえば先のhttp.Serverクラスの場合、クライアントからのリクエストを受信した場合「request」というイベントが、接続エラーが発生した場合は「clientError」というイベントが発生し、それぞれに対応するイベントハンドラが実行される。

 Node.jsのイベント機構はeventモジュールで定義されているEventEmitterクラスを使って実装されている。イベントを扱うクラスはすべてこのクラスの派生クラスとなっており、イベントハンドラの登録といったイベント関連の処理はこのEventEmitterクラスのメソッドを呼び出すことで実行する(表4)。それぞれのメソッドにおいてevent引数にはイベント名を示す文字列を、listener引数にはイベントハンドラとして使用する関数オブジェクトを指定する。

表4 EventEmitterクラスのメソッド
メソッド名および引数説明
addListener(event, listener)指定したイベントにイベントハンドラを登録する
on(event, listener)指定したイベントにイベントハンドラを登録する(addListerメソッドの別名)
once(event, listener)指定したイベントに一度だけ実行されるイベントハンドラを登録する
removeListener(event, listener)指定したイベントハンドラを削除する
removeAllListeners([event])指定したイベントのイベントハンドラをすべて削除する
setMaxListeners(n)登録できるイベントハンドラ数の上限を変更する。n引数には整数で上限数を指定する
listeners(event)指定したイベントに登録されているイベントハンドラを取得する
emit(event, [arg1], [arg2], […])指定したイベントを発生させる

イベントハンドラを登録する——emitter.onメソッド

 イベントに対してイベントハンドラを登録するには、onメソッドもしくはaddListenerメソッドを使用する。onメソッドとaddListenerメソッドは名前が異なるだけで、どちらも実行する処理は同一だ。そのため、以下ではonメソッドについてのみ解説する。

emitter.on(event, listener)
emitter.addListener(event, listener)

 onメソッドのevent引数にはイベント名、listener引数にはイベントハンドラとして使用する関数オブジェクトを指定する。たとえば次のコードでは、http.Server型のserverオブジェクトに対し、クライアントからのリクエストを受信したときに発生する「request」イベントを処理するイベントハンドラを登録している。

// requestイベントハンドラを定義する
server.on('request', function(request, response) {
  // コンソールにリクエストされたURLを出力
  console.log(request.url);
  // 200(OK)レスポンスと「Content-Type: text/plain」ヘッダを送信
  response.writeHead(200, {'Content-Type': 'text/plain'});
  // 「hello 」という文字列とともにリクエストURLを送信する
  response.end('hello ' + request.url);
});

 ここではイベントハンドラに無名関数を作成して与えている。イベントハンドラに与えられる引数はイベントに応じて異なるが、requestイベントの場合はリクエスト情報が格納されたrequestオブジェクトと、それに対するレスポンスを返すために使われるresponseオブジェクトが与えられる。無名関数内ではこれらを使用し、リクエストURLの表示やHTTPレスポンスの作成、といった処理を実行している。

 onメソッドを複数回実行することで、1つのイベントに対し複数のイベントハンドラを登録することも可能だ。

server.on('request', function(request, response) {
  console.log('request received.');
});
server.on('request', function(request, response) {
  console.log(request.url);
  response.writeHead(200, {'Content-Type': 'text/plain'});
  response.end('hello ' + request.url);
});
server.on('request', function(request, response) {
  console.log('response sent.');
});

 複数のイベントハンドラが登録されている場合、イベントの発生時には登録された順にイベントハンドラが呼び出される。上記の例では、requestイベントが発生したらまずコンソールに「request received.」という文字列が表示され、続いてクライアントにレスポンスを送信、最後にコンソールに「response sent.」という文字列が表示される。

 なお、デフォルトでは1つのイベントに対し登録できるイベントハンドラ数は10個までに制限されている。これは、誤ってイベントハンドラを多数登録してしまうことを防ぐためだ。この上限数はsetMaxListenersメソッドで変更できる。

emitter.setMaxListeners(n)

 n引数には上限として設定するイベントハンドラ数を整数で指定する。

 イベントに対し登録されているイベントハンドラを取得するには、listenersメソッドを使用する。

emitter.listeners(event)

 event引数にはイベントハンドラを取得したいイベント名を指定する。listenersメソッドは戻り値としてイベントハンドラとして登録されている関数オブジェクトを格納した配列を返す。

 onceメソッドはonメソッドと同様にイベントハンドラを登録するメソッドだが、onceメソッドで登録されたイベントハンドラは実行完了後に削除されるのが異なる点だ。言い換えると、onceメソッドで登録されたイベントハンドラは一度のみ実行され、それ以降同一のイベントが発生しても実行されない。onceメソッドの引数はonメソッドと同一だ。

emitter.once(event, listener)

イベントハンドラを削除する——emitter.removeListenerメソッド 

 登録したイベントハンドラを削除するには、removeListenerメソッドを使用する。

emitter.removeListener(event, listener)

 event引数には削除したいイベント、listener引数には削除したい関数オブジェクトを指定する。また、指定したイベントの全イベントハンドラを削除するにはremoveAllListenersメソッドを使用する。

emitter.removeAllListeners([event])

 event引数にはremoveListenerメソッドと同様に削除したいイベント名を指定する。また、event引数が省略された場合、すべてのイベントのイベントハンドラが削除される。

イベントを発生させる——emitter.emitメソッド

 イベントを発生させるにはemitメソッドを使用する。通常このメソッドをユーザーが明示的に呼ぶことはほぼないが、イベント機構を使用するクラスを実装する場合は、このメソッドを使ってイベントを発生させることになる。

emitter.emit(event, [arg1], [arg2], [...])

 event引数には発生させるイベント名を指定し、第2引数以降にはイベントハンドラに与える引数を指定する。

 なお、EventEmitter自身にも「newListener」というイベントが定義されている。これはオブジェクトに新たなイベントハンドラが登録された際に発生するイベントで、イベントハンドラの引数にはイベント名およびイベントハンドラとして登録された関数オブジェクトが与えられる。

【連載】はじめてのNode.js

本記事について

 本記事は、3月13日にソフトバンク クリエイティブより発売された書籍「はじめてのNode.js -サーバーサイドJavaScriptでWebアプリを開発する-」から、「第7章 Node.jsアプリケーションのデバッグ方法」の一部を抜き出し再構成したものです。

 出版社ページ / Amazon.co.jpの商品ページ

 大型本: 384ページ、価格:3,045円(税込)、ISBN: 978-4797370904