代码之家  ›  专栏  ›  技术社区  ›  Maciek

在Nodejs中管理大量回调递归

  •  18
  • Maciek  · 技术社区  · 16 年前

    在NodeJ中,实际上没有阻塞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..
    });
    

    许多的 回调。我很习惯使用回调,但我担心所有的递归。我是否有陷入太多递归并在某个地方破坏堆栈的危险?如果我用数千次回调对键值存储进行数千次单独写入,我的程序最终会崩溃吗?

    我是否误解或低估了影响?如果没有,在仍然使用Nodejs的回调编码风格的情况下,有没有办法解决这个问题?

    6 回复  |  直到 16 年前
        1
  •  24
  •   NickFitz    16 年前

    您显示的代码都没有使用递归。当你打电话的时候 useFile 它叫 posix.stat() ,返回,以及 在运行到完成时终止。在稍后的某个时间,当 posix.stat() 完成 在基础系统内 结果是可用的,您为此添加的回调函数将被执行。那叫 posix.open() ,然后在运行到完成时终止。成功打开文件后,的回调函数 那个 posix.read()

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

    代码的嵌套结构给人的印象是,内部的东西必须在外部的东西到达自己的终点之前完成。但是在这种异步事件驱动编程风格中,从以下方面来看嵌套更有意义: .

    编辑:尝试在每个嵌套函数结束之前添加一些日志记录语句;这将有助于说明它们完成的顺序是从外向内的。

        2
  •  3
  •   Jeoff Wilks    16 年前

    相同的示例,添加了调试输出(请参见下面的输出):

    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
    
        3
  •  3
  •   christkv    16 年前

    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
      }
    );
    
        4
  •  1
  •   TJ Holowaychuk TJ Holowaychuk    16 年前

        5
  •  1
  •   Ignacio Thayer    16 年前

    还可以看看“步骤”( http://github.com/creationix/step )或者github上的“flow js”。这使您能够以更自然的风格编写回调流。这也将清楚地表明没有递归在进行。

        6
  •  0
  •   Nosredna    16 年前

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