小林さん ありがとうございます。 恥ずかしながら見えていませんでした。 # 大津さんが仰っていたのもこれだったのですね。
取り急ぎお礼まで。 -- [email protected] (pc/mobile) mobile 090-1736-8716 @yuichirowada on twitter 2013/4/19 Koichi Kobayashi <[email protected]> > 小林 (koichik) です. > > ともあれ (JW)、一つ目の課題について自分が > 想定していた問題点を書いてみます。 > > サンプルのコードは以下の構造を持っています。 > > var s = fs.createReadStream(f).once('open', function () { > this.pipe(response); > }); > fs.stat(f, function(err, stats) { > s.on('data', function(data) { > }); > }); > > pipe() が使われていますが、その中では 'data' > (v0.10 以降では 'readable') イベントのリスナが > 登録されるので、それを表に出すと以下のように > なります。 > > var s = fs.createReadStream(f).once('open', function () { > //(1) > s.on('data', function(data) { > //(2) > }); > }); > fs.stat(f, function(err, stats) { > //(3) > s.on('data', function(data) { > //(4) > }); > }); > > ここで、(1)->(2) および (3)->(4) はこの順序で > 実行されますが、それぞれの間では順序の保証は > ありません。 > > そのため、もし fs.stat() に時間がかかると、 > (3) のところで 'data' リスナを登録するよりも > 先に、最初の 'data' イベントが発生してしまう > 可能性があります。 > > その場合、始めの方のデータは (2) の 'data' > リスナだけが受信し、(4) の 'data' リスナは > それを取りこぼすことになってしまいます。 > > これは現実的にはまず起きないとは思いますが、 > 似た状況は setTimeout() を作ることができます。 > > var s = fs.createReadStream(f).once('open', function () { > this.pipe(response); > }); > setTimeout(function() { > fs.stat(f, function(err, stats) { > s.on('data', function(data) { > }); > }); > }, 1000); //時間は適当 > > > これが一つ目の課題として想定した問題点です。 > > > 回避策としては、pause()/resume() を使うのが > 素直なやり方かと思います。 > > var s = fs.createReadStream(f).once('open', function () { > this.pipe(response); > }); > s.pause(); //一時停止 > fs.stat(f, function(err, stats) { > s.resume(); //再開 > s.on('data', function(data) { > }); > }); > > v0.8 以前の場合、pause() は「アドバイス的」な > ものということになっていますが、fs.ReadStream は > かなり初期から (たぶん v0.2 の頃から) 中断中は > 'data' イベントを生成せずにバッファリングしてくれて > いたのでこれで大丈夫。 > > v0.10 以降で導入された Stream2 では pause() は > 「アドバイス的」ではなくなったのでやっぱり > 大丈夫、そして pause() を呼び出すと "oldMode" に > 切り替わるので、後から 'data' リスナを追加しても > 安心になります。 > > > > On Wed, 17 Apr 2013 02:40:00 +0900, Koichi Kobayashi < > [email protected]> wrote: > > > 小林 (koichik) です. > > > > なぜ初心者には見えない人達が盛り上がってるのか > > 小一時間(ry > > > > ともあれ (JW)、翻訳書からのサンプルなので、 > > 時期的に原書が v0.10 に対応しているはずがなく、 > > ここでは Stream1 の話ということにした方が > > いいんじゃないかと思いますけれど。 > > > > > てことで、「fs.stat() も open イベント内部に押し込むと、意図した順序で処理してくれる。」てのが課題に対する私の解答。 > > > > んー、自分が想定 (期待) したのと違う問題を > > 解決しようとしてるようなw > > > > fs.stat() を 'open' イベントリスナの中に押し込むと、 > > fs.stat() のコールバックが呼び出されるタイミングは > > 元のコードよりさらに遅くなりますよね。 > > > > すると、キャッシュのバッファを埋めるための 'data' > > イベントのリスナを登録するのもさらに遅くなりますね。 > > それで大丈夫でしょうか? > > > > たとえば極端な例として、 > > > > var s = fs.createReadStream(f).once('open', function() { > > response.writeHead(200, headers); > > this.pipe(response); > > }); > > > > setTimeout(function() { > > s.on('data', function(buf) {...}); > > }, 10 * 1000); // 10 秒!!!! > > > > だったらどうでしょうか? > > setTimeout() の中で登録した 'data' リスナは > > 適切に呼び出されるでしょうか? > > > > > > On Tue, 16 Apr 2013 08:11:48 +0900, Akitoshi Manabe < > [email protected]> wrote: > > > > > 眞鍋です。 > > > > > > スミマセン。誤解してました。 > > > 以下のファイルを書いて試したところ、fs.createReadStream だと > > > readable イベントは発火しませんでした。 > > > > > > ---- (ここから) > > > var util = require('util'); > > > var fs = require('fs'); > > > var f = __filename; > > > > > > var s = fs.createReadStream(f, {bufferSize: 128 }); // default : 64 * > 1024 > > > [byte] > > > s.on( 'readable', function(){ util.puts( 'readable', arguments ) } > );//発火せず > > > s.on( 'open', function(){ > > > util.puts( 'open', arguments ) > > > // data イベントはopen後でも追加できる。 > > > s.on('data', function( d ){ util.puts('DATA ', d) }) > > > } ); > > > s.on( 'error', function(){ util.puts( 'error', arguments ) } ); > > > s.on( 'close', function(){ util.puts( 'close', arguments ) } ); > > > ---- (ここまで) > > > > > > 実は、readable イベントに押し込めるのではないかと考えていましたが、 > > > 「data イベントは open 直後に実行されるハンドラ内に定義しても良い」と確認できます。 > > > てことで、「fs.stat() も open イベント内部に押し込むと、意図した順序で処理してくれる。」てのが課題に対する私の解答。 > > > > > > > > > Nodeを始めた頃「関数のネスト(ピラミッドとも)で書く特徴」に悩まされました。 > > > 理解が深まってくると、ピラミッドをフラットで見やすく書く為の > > > 「フロー制御」を考えたくなると思います。 > > > > > > > > > > > > > > > 2013年4月16日 7:08 aporo4000 <[email protected]>: > > > > > > > 全然違うかもしれませんが、最初に読み込むバイト数を指定するとか!?ですか? > > > > > > > > 2013年4月16日火曜日 1時20分00秒 UTC+9 koichik: > > > >> > > > >> 小林 (koichik) です. > > > >> > > > >> 勝手に課題シリーズ第二弾w > > > >> 前のメールでも書いたように、非同期 API では > > > >> > > > >> ... //(1) > > > >> foo(hoge, function(err) { > > > >> ... //(2) > > > >> }); > > > >> ... //(3) > > > >> > > > >> の場合の処理順は (1)->(3)->(2) となります。 > > > >> > > > >> 問題のコードは HTTP を処理するリスナの中に > > > >> あるので、 > > > >> > > > >> http.createServer(function(req, res) { > > > >> ... //(1) > > > >> foo(hoge, function(err) { > > > >> ... //(2) > > > >> }); > > > >> ... //(3) > > > >> }); > > > >> > > > >> となるわけですが、ここで二つの HTTP リクエスト、 > > > >> A と B がほとんど同時に (ただし A が先に) 到着した > > > >> 場合の処理順を考えてみましょう。 > > > >> > > > >> その場合は、 > > > >> A(1)->A(3)->A(2)->B(1)->B(3)->B(2) となる。。。 > > > >> 保証はなくて、 > > > >> > > > >> A(1)->A(3)->B(1)->B(3)->A(2)->B(2) だったり > > > >> A(1)->A(3)->B(1)->B(3)->B(2)->A(2) だったり > > > >> する可能性もあります。 > > > >> リクエスト A に関する処理が全て完了してから > > > >> リクエスト B の処理が開始されるとは限らない > > > >> わけです。 > > > >> > > > >> さて、問題のコードからキャッシュの処理に着目すると、 > > > >> > > > >> http.createServer(function (request, response) { > > > >> //(1) > > > >> if(cache[f]) { > > > >> response.writeHead(200, headers); > > > >> response.end(cache[f].content); > > > >> return; > > > >> } > > > >> > > > >> fs.stat(f, function(err, stats) { > > > >> //(2) > > > >> var bufferOffset = 0; > > > >> cache[f] = {content: new Buffer(stats.size)}; > > > >> s.on('data', function(data) { > > > >> //(3) > > > >> data.copy(cache[f].content, bufferOffset); > > > >> bufferOffset += data.length; > > > >> }); > > > >> }); > > > >> }).listen(8080); > > > >> > > > >> という構造を抽出できるのですが、最初に到着した > > > >> リクエスト A の処理では、 > > > >> > > > >> (1) キャッシュが存在しないことを確認し、 > > > >> (2) キャッシュを作成して fs.stat() を呼び出し、 > > > >> (3) 'data' イベントが発生するとキャッシュの中身を設定 > > > >> > > > >> と流れていくことはもう理解できているかと思います。 > > > >> > > > >> では、この途中のどこかで別のリクエスト B が到着すると > > > >> どうなるでしょうか? > > > >> > > > >> A(1)->A(2)->A(3) > > > >> > > > >> と進む途中のどこかで、リクエスト B の処理が > > > >> 挟まってきた場合を考えてみましょう。 > > > >> > > > >> A(1)->B(1)->? > > > >> > > > >> とか、 > > > >> > > > >> A(1)->A(2)->B(1)->? > > > >> > > > >> とか、タイミングによっては B の処理が進む経路も > > > >> 変わってくるはずですね。 > > > >> > > > >> 掲載されたコードはどんな場合でも正しく > > > >> 動作するでしょうか? > > > >> うまくいかないのはどんな場合? > > > >> どんな場合でもうまく動くようにするに > > > >> どうしたらいいでしょうか? > > > >> > > > >> > > > >> On Mon, 15 Apr 2013 06:18:31 -0700 (PDT), aporo4000 < > [email protected]> > > > >> wrote: > > > >> > > > >> > いまちょうどそれを勉強してる初心者なので、みなさんみたいにスラスラ書いたり出来るように頑張ります! > > > >> > > > > >> > -- > > > >> > > > > >> > --- > > > >> > このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。 > > > >> > このグループから退会し、メールの受信を停止するには、[email protected] > にメールを送信します。 > > > >> > その他のオプションについては、https://groups.google.com/groups/opt_outにアクセスしてください。 > > > >> > > > > >> > > > >> > > > >> -- > > > >> { > > > >> name: "Koichi Kobayashi", > > > >> mail: "[email protected]", > > > >> blog: "http://d.hatena.ne.jp/koichik/", > > > >> twitter: "@koichik" > > > >> } > > > >> > > > >> -- > > > > > > > > --- > > > > このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。 > > > > このグループから退会し、メールの受信を停止するには、[email protected] > にメールを送信します。 > > > > その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。 > > > > > > > > > > > > > > > > > > -- > > > > > > --- > > > このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。 > > > このグループから退会し、メールの受信を停止するには、[email protected]にメールを送信します。 > > > その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。 > > > > > > > > > > > > -- > > { > > name: "Koichi Kobayashi", > > mail: "[email protected]", > > blog: "http://d.hatena.ne.jp/koichik/", > > twitter: "@koichik" > > } > > > > -- > > > > --- > > このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。 > > このグループから退会し、メールの受信を停止するには、[email protected]にメールを送信します。 > > その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。 > > > > > -- > { > name: "Koichi Kobayashi", > mail: "[email protected]", > blog: "http://d.hatena.ne.jp/koichik/", > twitter: "@koichik" > } > > -- > > --- > このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。 > このグループから退会し、メールの受信を停止するには、[email protected]にメールを送信します。 > その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。 > > > -- --- このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。 このグループから退会し、メールの受信を停止するには、[email protected] にメールを送信します。 その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。
