NodeJS 和异步地狱

Posted

技术标签:

【中文标题】NodeJS 和异步地狱【英文标题】:NodeJS and asynchronous hell 【发布时间】:2011-09-08 19:07:49 【问题描述】:

我刚遇到这种可怕的情况,我有一个字符串数组,每个字符串都代表一个可能存在的文件(例如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 的出色性能,但这是不可接受的。我有什么选择?

【问题讨论】:

这就是 node.js 的工作方式。确实,对我来说,这似乎并没有那么糟糕。在具有异步响应的任何环境中,您都会遇到完全相同的问题。 【参考方案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);

【讨论】:

【参考方案2】:

不要在普通的服务器环境中使用同步的东西——东西是单线程的,这会在等待这个 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 的额外调用,那么你可以很容易地编写一个函数“首先”执行操作,直到某些测试成功。类似于https://github.com/caolan/async#until - 但您对输出感兴趣。

【讨论】:

现在我喜欢这样。我会等着看有没有人想出更好的。这绝对看起来和感觉都非常可行。【参考方案3】:

异步库绝对是您正在寻找的。它以一种不错的异步方式提供了几乎所有您想要的迭代类型。不过,您不必编写自己的“第一个”函数。 Async 已经提供了一个 'some' 功能,可以做到这一点。

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

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

使用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));  
             
     
   ])

【讨论】:

以上是关于NodeJS 和异步地狱的主要内容,如果未能解决你的问题,请参考以下文章

如何优雅的处理Nodejs中的异步回调

如何优雅的处理Nodejs中的异步回调

解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!

回调地狱——JavaScript异步编程指南

[译] 回调地狱——JavaScript异步编程指南

基于先前调用的响应的异步调用,避免回调地狱