NodeJS和异步地狱

时间:2011-06-05 15:53:54

标签: javascript asynchronous node.js callback

我刚刚遇到这种可怕的情况,我有一个字符串数组,每个字符串代表一个可能存在的文件(例如var files = ['file1', 'file2', 'file3']。我需要遍历这些文件名并尝试查看它是否存在于当前目录中如果是这样,停止循环并忘记剩下的其余文件。所以基本上我想找到那些的第一个现有文件,如果没有找到,则回退到硬编码的消息。

这就是我目前所拥有的:

var found = false;
files.forEach(function(file) {
  if (found) return false;

  fs.readFileSync(path + file, function(err, data) {
    if (err) return;

    found = true;
    continueWithStuff();
  });
});

if (found === false) {
  // Handle this scenario.
}

这很糟糕。它阻塞(readFileSync)因此它很慢。

我不能只为fs.readFile提供回调方法,它不是那么简单,因为我需要获取第一个找到的项目......并且可以以任意随机顺序调用回调。我认为一种方法是使用回调来增加计数器并保留已找到/未找到信息的列表,当它达到files.length计数时,它会检查找到/未找到的信息并决定要做什么做下一步。

这很痛苦。我确实看到了事件IO的性能优势,但这是不可接受的。我有什么选择?

5 个答案:

答案 0 :(得分:14)

不要在普通服务器环境中使用同步内容 - 事物是单线程的,这会在等待这个io绑定循环的结果时完全锁定。 CLI实用程序=可能很好,服务器=启动时才可以。

异步流控制的通用库是 https://github.com/caolan/async

async.filter(['file1','file2','file3'], path.exists, function(results){
    // results now equals an array of the existing files
});

如果你想说,避免对path.exists的额外调用,那么你可以很容易地编写一个执行操作的'first'函数,直到一些测试成功。与https://github.com/caolan/async#until类似 - 但您对输出感兴趣。

答案 1 :(得分:2)

异步库绝对是您正在寻找的。它以很好的异步方式提供了你想要的几乎所有类型的迭代。但您不必编写自己的“第一个”功能。 Async已经提供了“某种”功能,可以完全实现这一功能。

https://github.com/caolan/async#some

async.some(files, path.exists, function(result) {
  if (result) {
    continueWithStuff();
  }
  else {
    // Handle this scenario
  }
});

如果您或将来阅读此内容的人不想使用Async,您也可以使用自己的基本版本'some。'

function some(arr, func, cb) {
  var count = arr.length-1;

  (function loop() {
    if (count == -1) {
      return cb(false);
    }
    func(arr[count--], function(result) {
      if (result) cb(true);
      else loop();
    });
  })();
}

some(files, path.exists, function(found) {
  if (found) { 
    continueWithStuff();   
  }
  else {
    // Handle this scenario
  }
});

答案 2 :(得分:2)

您可以使用递归函数在没有第三方库的情况下执行此操作。传递文件名和指针数组,最初设置为零。该函数应该检查数组中是否存在指定的(通过指针)文件名,并且在其回调中它应该执行其他操作(如果文件存在)或者递增指针并调用自身(如果文件没有)不存在)。

答案 3 :(得分:0)

使用async.waterfall来控制node.js中的异步调用,例如: 通过包含async-library并在异步中使用瀑布调用:

 var async = require('async'); 
 async.waterfall( 
   [function(callback) 
     { 
       callback(null, taskFirst(rootRequest,rootRequestFrom,rootRequestTo, callback, res));
     },
     function(arg1, callback) 
     {
       if(arg1!==undefined )
       {
         callback(null, taskSecond(arg1,rootRequest,rootRequestFrom,rootRequestTo,callback, res));  
       }      
     }
   ])

答案 4 :(得分:-1)

(编辑:删除了同步建议,因为它不是一个好主意,我们不希望任何人复制/粘贴它并在生产代码中使用它,我们会吗?)

如果您坚持使用异步内容,我认为实现此操作比您描述的更简单的方法是执行以下操作:

var path = require('path'), fileCounter = 0;

function existCB(fileExists) {
    if (fileExists) {
        global.fileExists = fileCounter;
        continueWithStuff();
        return;
    }
    fileCounter++;
    if (fileCounter >= files.length) {
        // none of the files exist, handle stuff
        return;
    }
    path.exists(files[fileCounter], existCB);
}

path.exists(files[0], existCB);