海未「最初に説明したように、Node.jsにはイベントループという仕組みが用いられています。それを理解する前段として、イベントについて見てみましょう」
ことり「イベントというのは、報酬として期間限定のSR部員を手に入れられる特別な催しで、メドレーフェスティバルやスコアマッチなどの種類があります」
穂乃果「報酬にはイベントポイントを稼ぐ達成報酬と順位を競うランキング報酬があり・・・」
海未「2人してずれた解説を始めないでください!」
海未「・・・イベントというのは、UIだと分かりやすいのですが何かのボタンを押したとか、一定時間が経ったとか、そういった何かのきっかけになり得るものを指します」
穂乃果「スコアマッチは課金のきっかけに・・・」
海未「ほーのーかー!」
穂乃果「ご、ごめんなさい・・・」
海未「イベント発生時の処理を記述していくという、イベントありきのプログラミングモデルをイベント駆動プログラミングと呼びます。Node.jsではこの形が主になります」
ことり「受け身なんだね」
海未「リアルタイム性が重視される局面では有利なスタイルですね。柔よく剛を制す、といったところでしょうか」
ことり「穂乃果ちゃんには向かないのかも」
穂乃果「むー・・・でも、そうかも」
海未「穂乃果の思考は一直線すぎるのです」
海未「例えば、前に見たhttp.createServer
がありましたね。再掲します」
1 2 3 4 5 6 7 |
var http = require("http"); http.createServer(function(req, res) { res.writeHead(200, {"Content-Type": "text/plain"}); res.end("ちゅんちゅん\n"); }).listen(25252, "127.0.0.1"); console.log("25252ポートで待ち受け中です"); |
海未「リクエストに対応するための処理を無名関数にして渡しているわけですが、その実行はcreateServer
の呼び出し時ではありませんし、listen
の呼び出し時でもありません」
ことり「ブラウザで見に行ったときなんだよね」
海未「はい。ブラウザで見に行く、つまりHTTPリクエストを送ると、内部的にrequest
イベントが発生します。すると、この関数が実行されます」
ことり「あ、なんとなくわかった」
海未「この関数はイベントリスナーやイベントハンドラと呼ばれ、request
イベントが発生する都度、何度でも実行されます」
ことり「システムが覚えちゃうんだね。条件反射みたい。海未ちゃんに鏡を見せると・・・♪」
穂乃果「loveArrowShoot
関数が実行されるんだねっ!」
海未「わ、私にそんなリスナーは登録されていません!」
海未「http.createServer
はあらかじめ用意されている仕組みを使ったものですが、もちろんイベントやそのリスナーは自由に定義することができます」
1 2 3 4 5 6 7 8 |
var events = require("events"); var umi = new events.EventEmitter(); umi.on("mirror", function() {console.log("ラブアローシュート!");}); umi.emit("mirror"); umi.emit("mirror"); |
ラブアローシュート!
ラブアローシュート!
穂乃果「・・・へー」
ことり「海未ちゃん、頑張ったね、よしよし」
海未「こ、これはその・・・分かりやすいかと思って、ことりの出した例をコードに落としたまでで・・・」
穂乃果「にやにや」
海未「・・・今私は、この愚行を深く悔いています」
海未「イベント関係を扱うにはeventモジュールを利用します。eventモジュールのEventEmitter
というオブジェクトを使って、イベントの登録や発火ができます」
ことり「これだと、海未ちゃんがEventEmitter
?」
海未「リスナーを登録し、それを発火させることができるオブジェクトですから、まあ、そうなりますね」
ことり「鏡がイベントなんだよね?」
海未「・・・そうです。イベントは単なる文字列で、on
で登録した文字列と同じものがemit
に渡されると、対応するリスナーが実行されます」
穂乃果「ラブアローシュート!だっ」
ことり「ラブアローシュート!だね」
海未「・・・もうやめたいです、この話・・・」
海未「1回しか実行されないイベントを作ることもできます」
1 2 3 4 5 6 7 8 |
var events = require("events"); var umi = new events.EventEmitter(); umi.once("mirror", function() {console.log("ラブアローシュート!");}); umi.emit("mirror"); umi.emit("mirror"); |
ラブアローシュート!
海未「リスナーの登録時にonce
を使うと、イベントが複数回発生してもリスナーは1回しか実行されません」
海未「EventEmitter
には複数のイベントを登録しておけます」
1 2 3 4 5 6 7 8 9 |
var events = require("events"); var muse = new events.EventEmitter(); muse.on("summer", function() {console.log("夏色えがおで1,2,Jump!")}); muse.on("summer", function() {console.log("Mermaid festa vol.1")}) muse.on("winter", function() {console.log("Snow halation")}); muse.emit("summer"); |
夏色えがおで1,2,Jump!
Mermaid festa vol.1
ことり「同じイベントに関連したリスナーが全部動くんだね」
海未「リスナーは削除することもできます」
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var events = require("events"); var muse = new events.EventEmitter(); muse.on("summer", function() {console.log("夏色えがおで1,2,Jump!")}); muse.on("summer", function() {console.log("Mermaid festa vol.1")}) muse.on("winter", function() {console.log("Snow halation")}); muse.emit("summer"); muse.removeListener("summer", muse.listeners("summer")[0]); muse.emit("summer"); |
夏色えがおで1,2,Jump!
Mermaid festa vol.1
Mermaid festa vol.1
海未「removeListener
の第2引数は削除するリスナーです。名前付きの関数だったり、関数式で変数に代入してあればそれを指定すればよいのですが、インラインで書いた無名関数の場合少々面倒です」
穂乃果「渡したきりで、手元に残ってないもんね」
海未「そういう場合はlisteners
メソッドで全リスナーを配列で取得できますから、それを利用します」
海未「イベントについての基礎知識はこのくらいです。次回はこれを発展させて、Node.jsのイベントループについて見てみましょう」