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 和异步地狱的主要内容,如果未能解决你的问题,请参考以下文章