EventEmitter.emit() によくある勘違い
突然ですが、Node.jsで次のプログラムを実行した結果を答えてください。
var EventEmitter = require('events').EventEmitter; var event = new EventEmitter(); console.log('1'); event.on('open', function () { console.log('2'); }); event.emit('open'); console.log(3);
正解は少し下の方に書いてあります。
少しだけスクロールを我慢して考えてみてください。
正解は
1 2 3
です。
1 3 2
だと思っていた人も多いのではないでしょうか。(私だけかもしれませんが)
つまり、表題の「EventEmitter.emit() によくある勘違い」とは、EventEmitter.emit()が次のイベントループで実行されるノンブロッキング処理である、という点です。しかし、EventEmitter.emit()はコールバックを同期呼出しする、つまり、ブロッキングします。
では、1, 3, 2 と出力したい場合は、どう書くかというと、次のように書きます。
var EventEmitter = require('events').EventEmitter; var event = new EventEmitter(); console.log('1'); event.on('open', function () { process.nextTick(function () { console.log('2'); }); }); event.emit('open'); console.log(3);
もしくはこう。
var EventEmitter = require('events').EventEmitter; var event = new EventEmitter(); console.log('1'); event.on('open', function () { console.log('2'); }); process.nextTick(function () { event.emit('open'); }); console.log(3);
開発者はコールバック関数の中でprocess.nextTick()を呼び出すか、emit()自体をprocess.nextTick()のコールバック内で呼び出すことで、処理を次のイベントループに回すことができます。 emit()がコールバックを同期呼び出しするのは、開発者に処理をブロッキングで処理するのか、ノンブロッキングで処理するのかの選択肢を与えるために、意図してそのようにデザインされているのです。
考えてみれば、当たり前のことなのですが、自分がついこの間まで勘違いしていたので、戒めも兼ねて書いてみました。 みなさんも思い込みには気をつけて、必ず動作を確認するようにしましょう。