在Nodejs中管理大量的回调递归

时间:2009-11-27 16:24:43

标签: javascript recursion callback node.js v8

在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的回调编码风格?

6 个答案:

答案 0 :(得分:24)

您显示的代码都没有使用递归。当您致电useFile时,它会调用posix.stat(),并返回useFile,因为它已经运行完成。稍后,当对posix.stat()的调用已完成在基础系统中并且结果可用时,您为此添加的回调函数将被执行。调用posix.open(),然后在运行完成时终止。文件成功打开后, 的回调函数将执行,调用posix.read(),然后终止,因为它也已运行完成。最后,当读取结果可用时,将执行最里面的函数。

重要的一点是每个函数都运行完成,因为对posix.*()函数的调用是非阻塞的:也就是说,它们会立即返回,导致在底层系统中启动一些魔法。因此,每个函数都会终止,稍后事件将导致下一个函数执行;但是没有任何递归。

代码的嵌套结构可以给人一种印象,即在外面的东西可以到达自己的终点之前,内部的东西必须完成。但是在这种异步事件驱动编程风格中,根据 deep =>来看待嵌套更有意义。发生晚于如下

编辑:尝试在每个嵌套函数结束之前立即添加一些日志语句;这将有助于说明它们完成的顺序来自外部。

答案 1 :(得分: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

答案 2 :(得分: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
  }
);

答案 3 :(得分:1)

你的东西很好。我在Express中进行递归调用以跟踪HTTP重定向,但你做的是“遍历”而不是递归

答案 4 :(得分:1)

另请参阅github上的'step'(http://github.com/creationix/step)或'flow-js'。这使您可以以更自然的方式编写回调流。这也将表明没有递归。

答案 5 :(得分:0)

与任何JavaScript一样,可以使用Node.js进行递归调用。如果你遇到了递归深度问题(正如NickFitz指出的那样,你似乎没有这种危险),你可以经常重写你的代码来使用间隔计时器。