はじめてのNode.js:Node.jsのイベントシステムを知る 2ページ
さまざまな機能が実装された「モジュール」を使う
Node.jsにはさまざまな機能が実装された標準ライブラリが用意されており、これらは機能ごとに「モジュール」にまとめられている。
モジュールを読み込む——require関数
モジュールを読み込んで利用可能にするにはrequire関数を使用する。
require(moduleName)
moduleName引数には、ロードしたいモジュールの名前を文字列で指定する。require関数は指定されたモジュールを読み込み、そのモジュールに含まれるメソッドやクラスといったコンテンツを格納したオブジェクトを返す。require関数を実行したプログラムからは、このオブジェクトを通じてモジュール内の関数やクラスなどにアクセスできる。
たとえば「http」というモジュールを利用するには、次のようにする。
var http = require('http');
なお、REPL内ではrequire関数を使用せずとも、モジュール名を入力するだけでそのモジュールをロードできる。たとえばREPL内では「http = require(‘http’)」とする代わりに次のようにしてhttpモジュールをロードできる。
> http
読み込んだモジュール内の関数を呼び出すには、.演算子を使用する。たとえばhttpモジュールに含まれるcreateServer関数を実行するには次のようにする。
var server = http.createServer();
createServer関数はHTTPサーバー(http.Server)クラスのインスタンスを作成する関数で、戻り値としてhttp.Server型のオブジェクトを返す。
Node.jsのコアモジュール
Node.jsに付属しているモジュールは「コアモジュール」と呼ばれている。Node.js 0.8系では、表1のコアモジュールが用意されている。
モジュール名 | 提供される機能 | Stability(安定度) |
---|---|---|
assert | アサーション | 5 |
buffer | バイト列の格納および操作 | 3 |
child_process | 子プロセスの生成や管理 | 3 |
cluster | 複数のプロセスを使った負荷分散 | 1 |
console | コンソールへのメッセージ出力 | 4 |
crypto | 暗号化/ハッシュ | 2 |
dgram | UDPを扱うソケット関連の処理 | 3 |
dns | DNS関連の処理 | 3 |
domain | 複数のIO処理間の連携 | 1 |
events | イベント処理を実装するための基底クラス | 4 |
fs | ファイルおよびファイルシステムの操作 | 3 |
http | HTTPサーバー/クライアント | 3 |
https | HTTPSサーバー/クライアント | 3 |
net | ソケットの操作 | 3 |
os | OSに関連する情報の取得 | 4 |
path | パス文字列の処理 | 3 |
punycode | Punycode文字列のエンコード/デコード | 2 |
querystring | HTTPで使われるクエリ文字列の処理 | 3 |
readline | 標準入出力を使用した対話的インターフェイス | 2 |
repl | REPL | (なし) |
stream | ストリーム入出力処理が定義された基底クラス | 2 |
string_decoder | バイナリ列から文字列へのデコード | 3 |
tls | OpenSSLを使ったTLS/SSL通信 | 3 |
tty | TTY(キャラクタ端末)の情報取得 | 2 |
url | URL文字列のパースやフォーマット | 3 |
util | 各種ユーティリティ関数 | 5 |
vm | JavaScriptの実行エンジン(仮想マシン、VM) | 2 |
zlib | zlibを使ったデータの圧縮/伸張機能 | 3 |
コアモジュールの詳細については、Node.jsのドキュメント(http://nodejs.org/api/)で参照できる。なお、「Stability(安定度)」というのは、その名のとおりそのモジュールがどのくらい安定しているかの完成度を表すもので、1~5の5段階が用意されている(表2)。ただ、安定度が低いからといって使い物にならないというわけではなく、Unstableだからといって使用を控えるべき、ということではない。
レベル | 説明 |
---|---|
1 | Experimental(実験的実装) |
2 | Unstable(不安定) |
3 | Stable(安定) |
4 | API Frozen(今後APIの変更は行われない) |
5 | Locked(今後変更は行われない) |
また、Node.jsではJavaScriptエンジンとして使用している「V8」がサポートしているECMAScript 5で定義されている関数やクラスに加え、いくつかのクラスや関数、オブジェクト、関数が追加されている(表3)。これらは特定のモジュールをロードすることなしに利用できる。
クラス/関数/オブジェクト名 | 説明/処理内容 |
---|---|
Bufferクラス | バイト列を扱うクラス |
setTimeout関数 | 指定した時間が経過したときに指定した関数を実行する |
clearTimeout関数 | setTimeoutで設定した関数の実行を取り消す |
setInterval関数 | 指定した一定間隔で指定した関数を実行する |
clearInterval関数 | clearInterval関数で設定した関数の実行を取り消す |
require関数 | モジュールをロードする |
moduleオブジェクト | モジュール情報にアクセスするためのオブジェクト |
processオブジェクト | プロセス情報にアクセスするためのオブジェクト |
モジュールを自作したり、サードパーティが公開しているモジュールを利用することも可能だ。
ECMAScriptとは
JavaScriptを標準化するための国際規格。1999年に策定されたECMAScript 3が長らく標準規格として使われていたが、2009年にさまざまな拡張が行われたECMAScript 5がリリースされた。ECMAScriptではJSONデータを扱うJSONクラスの追加やObjectクラスの拡張などが行われている。
非同期処理とコールバック関数——synchronusとasynchronous
Node.jsの関数には、「同期的(synchronous)な関数」と、「非同期的(asynchronous)な関数」がある。呼び出された際、その処理の実行が完了するまでは関数の呼び出し元には戻らない関数のことを同期的な関数と呼び、console.log関数などがこれに該当する。いっぽう、呼び出された際、関数内で実行すべき処理が完了する前に呼び出し元に戻る関数のことを「非同期的な関数」と呼ぶ。リスト3-1の最後で呼び出しているserver.listenメソッドも非同期的な関数である。このメソッドはHTTPサーバーの待ち受けを開始させるもので、この関数自体はすぐに終了するが、バックグラウンドでは待ち受け処理が継続して実行されている。
Node.jsプログラムでは、このような「非同期的な関数」が多用されるのが特徴だ。そして非同期的な関数では、関数が処理を完了する前に戻り値を返すため、関数の戻り値でその結果を受け取ることができない。そのため、非同期的な関数や非同期的なメソッドを持つクラスには「コールバック」や「イベント」といった処理結果を通知するための機構が用意されている。コールバックは、関数の引数として「コールバック関数」と呼ばれる別の関数を指定しておき、処理の完了時にその結果をコールバック関数の引数として与えて実行する仕組みだ。
たとえばファイルからその内容を読み出す、といった処理を考えてみよう。Node.jsには、ファイルに対する操作をまとめたfsモジュールが用意されており、ファイルからその内容を読み出すにはfsモジュールに含まれるreadFile関数が利用できる。
fs.readFile(filename, [encoding], [callback])
readFile関数のfilename引数にはファイル名を、encoding引数には読み出すデータのエンコーディングを、第3引数にコールバック関数を指定する。
readFile関数は呼び出すと即座にundefinedを返し、バックグラウンドでファイルからの読み出しを実行する。そしてデータの読み出しが完了し、スレッドがアイドル状態になったタイミングで指定したコールバック関数が実行される。このとき、コールバック関数には(err, data)という引数が与えられる。err引数には処理が成功した場合nullが、失敗した場合にはエラーに関する情報が格納されたエラーオブジェクトが与えられる。また、data引数には読み出したデータが与えられる。
REPLを使って実際の挙動を確かめたものが以下の実行例となる。ここでは「/etc/passwd」というファイルを読み出し、読み出し完了後に「read!」というメッセージと、読み出したファイルの内容を表示させている。
> var fs = require('fs'); undefined > function test01() { ... fs.readFile('/etc/passwd', 'utf8', function (err, data) { ..... console.log('read!'); ..... console.log(data); ..... }); ... console.log('foo'); ... console.log('bar'); ... for (var i = 0; i < 10000; i++) { ..... console.log('.'); ..... } ... } undefined > test01() foo bar . . : : . . undefined > read! ## # User Database : :(/etc/passwdファイルの内容が表示される) :
ここで注目してほしいのが、fs.readFile関数の実行後、「console.log(‘foo’)」や「console.log(‘bar’)」、そしてforループ内の処理が完了してからコールバック関数内に記述されている処理が実行されている点だ。/etc/passwdファイルのファイルサイズは数キロバイト程度でありファイルの読み込み自体はほぼ一瞬で完了するにも関わらず、readFile関数に続く処理が完了するまでコールバック関数は実行されない。このような挙動となるのは、Node.jsが基本的にシングルスレッドでプログラムを実行しているためだ。この挙動を図示すると、図3のようになる。
Node.jsでは実行すべき関数をキューのようなもので管理しており、実行している処理が終了してアイドル状態となった時点で、キュー内に入っている次の関数を実行するようになっている。そのため、非同期な関数によってバックグラウンドで行われている処理が完了しても、別の関数が実行されている間は対応するコールバック関数は実行されない。言い換えれば、原則として複数の関数が並列として実行されるという状況は発生しないのである。そのため、複数の関数が同時に同じ変数にアクセスすることによる「競合」やロックなどを気にする必要はない。
Node.jsではreadFile関数以外にもコールバックを用いる関数が多く用意されている。その場合もreadFile関数と同様、呼び出されるコールバック関数の第1引数にはエラーオブジェクトが、第2引数以降には処理結果を含むオブジェクトが与えられるのが一般的だ。