18

Nodejs では、事実上ブロッキング I/O 操作はありません。これは、ほとんどすべての nodejs IO コードが多くのコールバックを含むことを意味します。これは、データベース、ファイル、プロセスなどの読み書きに適用されます。典型的な例は次のとおりです。

var useFile = function(filename,callback){
    posix.stat(filename).addCallback(function (stats) {
        posix.open(filename, process.O_RDONLY, 0666).addCallback(function (fd) {
            posix.read(fd, stats.size, 0).addCallback(function(contents){
                callback(contents);
            });
        });
    });
};

...

useFile("test.data",function(data){
    // use data..
});

多くのIO 操作を行うコードを書くことを期待しているので、多くのコールバックを書くことを期待しています。コールバックの使用にはかなり慣れていますが、すべての再帰が心配です。再帰が多すぎて、どこかでスタックを吹き飛ばす危険がありますか? 何千ものコールバックを使用して Key-Value ストアに何千もの個別の書き込みを行った場合、プログラムは最終的にクラッシュしますか?

影響を誤解または過小評価していませんか? そうでない場合、Nodejs のコールバック コーディング スタイルを使用しながら、これを回避する方法はありますか?

4

6 に答える 6

24

あなたが示すコードはどれも再帰を使用していません。を呼び出すuseFileposix.stat()、 が呼び出され、実行が完了すると戻り、useFile終了します。後で、基になるシステム内でposix.stat()への呼び出しが完了し、結果が利用可能になると、そのために追加したコールバック関数が実行されます。それは を呼び出し、実行が完了すると終了します。ファイルが正常に開かれると、そのコールバック関数実行され、 が呼び出され、実行が完了すると終了します。最後に、読み取りの結果が利用可能になると、最も内側の関数が実行されます。posix.open()posix.read()

重要な点は、posix.*()関数の呼び出しがノンブロッキングであるため、各関数が最後まで実行されることです。したがって、各関数が終了し、後でイベントによって次の関数が実行されます。しかし、再帰はありません。

コードのネストされた構造は、外側のものが独自の終点に到達する前に、内側のものを終了する必要があるという印象を与える可能性があります。しかし、このスタイルの非同期イベント駆動型プログラミングでは、ネストをより深い => occur-later-thanという観点から見る方が理にかなっています。

編集: ネストされた各関数の終了直前にいくつかのログ ステートメントを追加してみてください。これは、それらが完了する順序が外側から内側に向​​かっていることを示すのに役立ちます.

于 2009-11-27T16:42:11.533 に答える
3

あなたが試すことができます

http://github.com/creationix/do

または、私がしたように自分でロールします。今のところ、エラー処理の欠落は気にしないでください (無視してください) ;)

var sys = require('sys');

var Simplifier = exports.Simplifier = function() {}

Simplifier.prototype.execute = function(context, functions, finalFunction) {
  this.functions = functions;
  this.results = {};
  this.finalFunction = finalFunction;
  this.totalNumberOfCallbacks = 0
  this.context = context;
  var self = this;

  functions.forEach(function(f) {
    f(function() {
      self.totalNumberOfCallbacks = self.totalNumberOfCallbacks + 1;
      self.results[f] = Array.prototype.slice.call(arguments, 0);     
      if(self.totalNumberOfCallbacks >= self.functions.length) {
        // Order the results by the calling order of the functions
        var finalResults = [];
        self.functions.forEach(function(f) {
          finalResults.push(self.results[f][0]);
        })
        // Call the final function passing back all the collected results in the right order 
        finalFunction.apply(self.context, finalResults);
      }
    });
  });
}

そしてそれを使った簡単な例

// Execute 
new simplifier.Simplifier().execute(
  // Context of execution
  self,  
  // Array of processes to execute before doing final handling
  [function(callback) {
      db.collection('githubusers', function(err, collection) {
        collection.find({}, {limit:30}, function(err, cursor) {
          cursor.toArray(function(err, users) { callback(users); })
        });
      });      
    },

    function(callback) {
      db.collection('githubprojects', function(err, collection) {
        collection.find({}, {limit:45, sort:[['watchers', -1]]}, function(err, cursor) {
          cursor.toArray(function(err, projects) { callback(projects); })
        });
      });              
    }
  ],  
  // Handle the final result
  function(users, projects) {
    // Do something when ready
  }
);
于 2010-03-14T01:35:38.403 に答える
3

デバッグ出力を追加した同じ例 (出力については以下を参照):

usefile.js:

var sys = require("sys"),
  posix = require("posix");

var useFile = function(filename,callback){
    posix.stat(filename).addCallback(function (stats) {
        posix.open(filename, process.O_RDONLY, 0666).addCallback(function (fd) {
            posix.read(fd, stats.size, 0).addCallback(function(contents){
                callback(contents);
                sys.debug("useFile callback returned");
            });
            sys.debug("read returned");
        });
        sys.debug("open returned");
    });
    sys.debug("stat returned");
};

useFile("usefile.js",function(){});

出力:

DEBUG: stat returned
DEBUG: open returned
DEBUG: read returned
DEBUG: useFile callback returned
于 2010-01-21T12:57:31.133 に答える
1

また、githubの「step」(http://github.com/creationix/step)または「flow-js」もご覧ください。これにより、より自然なスタイルでコールバックフローを記述できます。これにより、再帰が発生していないことも明確になります。

于 2010-06-24T18:48:06.990 に答える
1

あなたのものは大丈夫です。HTTPリダイレクトに従うためにExpressで再帰呼び出しを行いますが、あなたがしているのは「トラバーサル」であり、再帰ではありません

于 2009-12-18T01:59:12.020 に答える
0

他の JavaScript と同様に、Node.js を使用して再帰呼び出しを行うことができます。再帰の深さの問題が発生した場合 (NickFitz が指摘しているように、その危険はないようです)、多くの場合、代わりにインターバル タイマーを使用するようにコードを書き直すことができます。

于 2009-12-01T22:00:36.013 に答える