7

私はnode.jsを勉強していて、node.jsマニュアルでこの例に出くわしました:

...
var req = http.request(options);
req.end();

req.on('upgrade', function(res, socket, upgradeHead) {
  console.log('got upgraded!');
  socket.end();
  process.exit(0);
});
...

この例で私が見ているのは、HTTP リクエストのイベントにハンドラーがアタッチされていることです。リクエストが作成された後、さらには送信 (予定) された後でもあります。さらに悪いことに、マニュアルには次のように書かれています。

このイベントがリッスンされていない場合、アップグレード ヘッダーを受信するクライアントの接続は閉じられます。

ハンドラーをアタッチする前に イベントが発生する可能性はありませんか? req.on(...ノードの非同期モデルで何かを理解していないのではないかと思います。または、ネットワーク要求が次のコード行の実行よりも長くかかることを期待して設計されたノードマニュアルのこのコード?!

もう一つの例:

http.get("http://www.google.com/index.html", function(res) {
  console.log("Got response: " + res.statusCode);
}).on('error', function(e) {
  console.log("Got error: " + e.message);
});

ここでは、オブジェクトが作成された直後に HTTP リクエストが開始され、その後にのみエラー ハンドラがアタッチされます。繰り返しますが、(1) ネットワークの遅延が原因でのみ機能するコードですか、(2) node.js の概念について何もわかりません、または (2b) ハンドラーをアタッチするまでイベントは「待機」しますか?

編集:マニュアルからのさらに良い例。以下の良い例と悪い例が異なるのは、良い例ではイベントを十分に迅速にアタッチするため、データを見逃す可能性が低いか、またはこの方法でデータを見逃す可能性がまったくないためです (なぜ?!)

// Good
request.on('response', function (response) {
  response.on('data', function (chunk) {
    console.log('BODY: ' + chunk);
  });
});

// Bad - misses all or part of the body
request.on('response', function (response) {
  setTimeout(function () {
    response.on('data', function (chunk) {
      console.log('BODY: ' + chunk);
    });
  }, 10);
});
4

2 に答える 2

6

あなたが見逃しているのは、JavaScript がまったく非同期ではないということです! 私が言いたいのは、JavaScript はシングルスレッドであり、非同期操作は実際には非同期ではないということです。JavaScript が非同期であるかのような錯覚を与える非常に手の込んだキュー モデルがあります (誤解しないでください。これは依然として最も効率的なモデルです)。

それで、それはどういう意味ですか?これは、同期コードが実行されると、他のコードを並行して実行することは不可能であることを意味します。たとえば、このコードでは

var req = http.request(options);
req.end();
req.on(...);

リクエストはスケジュールされていますが、メイン スレッド (つまり操作) は に終了していませんreq.end()。メイン操作が完了していない限り、その間に非同期コードを起動することはできません。特に、ハンドラーは、実際のイベントが発生する前に常に設定されます。

もう少しわかりやすくするために、もう 1 つの例を示します。次のコードを検討してください。

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
    while(true) { } // <------ infinite loop doing nothing
}).listen(1337, '127.0.0.1');

最初のリクエストは成功して終了することに注意してください。しかし、それ以外のリクエストには応答がありません。これは、ループが操作を終了せず、JavaScriptが別のイベントにジャンプできないためです。このコードは、アプリを完全にクラッシュさせます。そのため、Node.js との同期コードには注意してください。:)

于 2012-07-23T14:36:08.490 に答える
3

理解しておくべき重要なことは、イベントループは1つだけであり、非同期関数を呼び出すと、コントロールはイベントループの現在の「ティック」のみを残すということです。通常、これはI / O(コードの数行ごとが一般的)を実行するか、setTimeout/を呼び出すときに自然に発生します。setInterval(かなりまれです)。すべてのイベントハンドラーがイベントループの同じティック内に登録されている限り、データが失われることはありません。さらに、イベントループのそのティック内では、ハンドラーをアタッチする順序は重要ではありません。そのティックの間、コードは文字通りノードが実行している唯一のものであり、他のコードはI/Oを受信したりイベントを呼び出したりすることはできません。現在のイベントループティックが完了するまでハンドラー。「十分に長く」待機したり、ネットワークの遅延などを行ったりすることではありません。イベントハンドラー関数に関して予測可能な操作を保証するのは、単一のイベントループの単純さです。

于 2012-07-23T14:43:19.773 に答える