给回调函数添加额外参数

5

我正在使用Node.js构建一个系统,该系统旨在查找数组中所有文件夹中的所有文件,对其进行统计,然后使用该信息进行一些额外的工作。

我正在使用fs.readdir()同步地从每个文件夹中获取所有文件。我的代码如下:

for(i=0,max=paths.length; i<max; i++) {
    var path = paths.pop();
    console.log("READING PATH: " + path);
    fs.readdir(path, function(err, files) { handleDir(err, files, path); });
}

问题在于,根据readdir()的执行速度不同,handleDir()得到了错误的路径。这是因为在回调函数运行时,下一个循环已经开始,也就是说路径变量已经改变。
所以,我需要想办法将该路径变量锁定到其特定的回调函数中。我想不出任何好的方法来做到这一点 - 有人有什么想法吗?
5个回答

7

在 JavaScript 中没有块级作用域,因此需要使用函数来创建作用域。

for(var i=0, path, max=paths.length; i<max; i++) {
    path = paths.pop();
    console.log("READING PATH: " + path);
    handlePath( path );
}
function handlePath ( path ) {
    fs.readdir(path, onPathRead);
    function onPathRead (err, files) {
        handleDir(err, files, path);
    }
}

5

这是我觉得JS中比较烦人的部分之一。一个替代方案是将代码包装在一个匿名函数中,在路径变量改变之前执行。

for(i=0,max=paths.length; i<max; i++) {
    var path = paths.pop();
    console.log("READING PATH: " + path);
    fs.readdir(path,
        (function(p){
            return function(err, files) {
                handleDir(err, files, p);
            };
        })(path);
    );
}

无论如何,重要的是在重新分配path变量的值之前,函数的上下文(匿名或非匿名)被初始化。

我喜欢这种技术,因为它简洁明了。 - nicolaskruchten

1

这确实是Javascript的一个令人讨厌的特性,以至于Coffeescript(一种编译成Javascript的语言)有一种特定的处理方式,即使用for上的do运算符。在Coffeescript中,您的原始函数将如下所示:

for path in paths
     fs.readdir path, (err, files) -> handleDir(err, files, path)

而你的问题的解决方案是:

for path in paths
   do (path) ->
     fs.readdir path, (err, files) -> handleDir(err, files, path)

这只是编译成与上面两个答案相同的东西吗? - jwegner
多多少少,是的,我只是想插入这个(依我之见)非常优雅简洁的解决方案 :) - nicolaskruchten

0

generalhenry的解决方案很棒,但是如果你要在回调函数内部使用try/catch结构,请小心。

function handlePath ( path ) {
    fs.readdir(path, onPathRead);
    function onPathRead (err, files) {
        try {
            handleDir(err, files, path);
        } catch (error) {
            var path = 'something_else'; // <<--- Never do this !!!
        }     
    }
}

永远不要在catch块中尝试重新声明同一个变量,即使该catch块从未被调用,该变量也会被重置为'undefined',当回调函数执行时,你会发现它无法使用。 尝试这个简单的例子:
function wrapper(id) {
    console.log('wrapper id:' + id);
    setTimeout(callback, 1000);
    function callback() {
        try {
            console.log('callback id:' + id);
        } catch (error) {
            var id = 'something_else';
            console.log('err:' + error);
        }
    }
}

wrapper(42);

这将输出:
wrapper id:42
callback id:undefined

0

我也曾经寻找同样的东西,最终找到了解决方案。这里提供一个简单的示例,如果有人想要了解,请参考以下内容。

var FA = function(data){
   console.log("IN A:"+data)
   FC(data,"LastName");
};
var FC = function(data,d2){
   console.log("IN C:"+data,d2)
};
var FB = function(data){
   console.log("IN B:"+data);
    FA(data)
};
FB('FirstName')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接