node.jsのEventEmitterについてのメモ書き

ChainZ(クリエイター)
いろいろやってます。

EventEmitterはいつ使うの?

非同期のJavaScriptにはCallbackパタンがよく使われてる。Node.js開発経験のある方ならわかると思うが、処理が長くなると、コードが階段状になりがちなのだ:

callme(function(){
callme(function(){
callme(function(){
callme(function(){
done();
});
});
});
});

Callbackの処理が簡単な場合は、この階段状のコードでも特に問題ないだが、もし複雑の処理になると、かなり読みづらい。そこで、EventEmitterを使って処理結果をEventとして外に出すのが解決方の一つ。

EventEmitterの使い方

EventEmitterはnode.jseventsモジュールの中に入ってる。EventEmitterを継承することで、onemitメソードでEventの受け取りと転送ができる。サンプルを作成してみよう:

var util = require('util');
var events = require('events');
var fs = require('fs');
var async = require('async');
// Event名が正しさを確保するために、変数を使う
var _e = {
data : 'data',
end : 'end',
error: 'error, </div></div>
};
// DirReaderクラス
function DirReader(dir){
event.EventEmitter.call(this);
// dirをセット
this.dir = dir;
}
util.inherits(DirReader, events.EventEmitter);
DirReader.prototype.read = function() {
var self = this;
var count = 0;
// ディレクトリーに入ってるファイルを読み出す
fs.readdir(this.dir, function(err, files) {
// エラーを直接throwするより、「error」イベントとして外にだす
if (err) return self.emit(_e.error, err);
// すべてのファイル中身を順番に読み出す
async.eachSeries(files, function(file, done) {
fs.readFile(file, function(err, data) {
if (err) return done(err);
// 無事に読み出せば、データを「data」イベントとして外にだす
self.emit(_e.data, data);
count++;
done();
});
}, function(err) {
if (err) return self.emit(_e.error, err);
// 処理終了を「end」イベントとして出す。
self.emit(_e.end, count);
});
});
};</span>
</pre></div></figure> この`DirReader`モジュールは下記のように使える:
var reader = DirReader('./path');
reader.on('data', function(data){
console.log(data);
});
reader.on('end', function(count){
console.log('ファイル' + count + '個を処理しました。');
});
reader.on('error', function(err){
console.log(err);
});
**処理結果をイベント形式で外に出すと、複雑な処理をやってもコードの可読性を保てる**。実はnode.jsのapiにもこのパターンが大量に使われている。例えば、streamとか〜 > `on`のほかには`once`というメーソドがある。`on`と違って、`once`はそのイベントを一回だけ受け取って、そのあと同じイベントが転送してきたら、無視するという挙動になる。 ## EventEmitterの応用 `EventEmitter`を使う理由は、**コードの可読性がよくなるだけじゃなく、同じイベントに複数の処理が行えるというメリットもある**。例えば先ほどのサンプルで、`DirReader`で読み出したデータをconsoleに出す同時に、データベースにも保存したい場合はどうするんだろう?もう一個`on`を作って`data`イベントを購読すれば良いのだ:
var reader = DirReader('./path');
reader.on('data', function(data){
// consoleにだす
console.log(data);
});
reader.on('data', function(data){
// データベースに入れる
var file = new File({body: data});
file.save();
});
reader.on('end', function(count){
console.log('ファイル' + count + '個を処理しました。');
});
reader.on('error', function(err){
console.log(err);
});
簡単でしょ?もちろん、consoleに出すなら、`on`を一個用意するほどでもないが、ニュアンスが伝えられたらと思う。 ## まとめ **`EventÉmitter`を使うことで、コードが読みやすくなるし、今後システムの拡張などにも十分な柔軟性を持つ**ので、node.jsでの開発を次のレベルに向かうなら、ぜひ`EventEmitter`を活用してください。