質問

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 操作なので、書くことになると思います 多くの コールバック。私はコールバックの使用に非常に慣れていますが、すべての再帰が心配です。再帰が多すぎて、どこかでスタックがパンクしてしまう危険はありますか?何千ものコールバックを使用してキーと値のストアに何千もの個別の書き込みを行った場合、プログラムは最終的にクラッシュしますか?

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

役に立ちましたか?

解決

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

重要な点は、各関数が、 posix.*() 関数はノンブロッキングです。つまり、それらはすぐに戻り、基礎となるシステムで何らかの魔法が開始されることになります。したがって、各関数が終了し、その後のイベントによって次の関数が実行されます。しかし、どの時点でも再帰はありません。

コードの入れ子構造により、外部のコードが独自のエンドポイントに到達する前に、内部のコードが終了する必要があるような印象を与える場合があります。しかし、このスタイルの非同期イベント駆動型プログラミングでは、ネストを次の観点から見る方が合理的です。 より深い => より後で起こる.

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

他のヒント

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

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

試すことができます

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
  }
);

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

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

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top