Node.js:回调中变量范围的问题

时间:2011-08-06 21:39:59

标签: javascript node.js callback

var result = { controllers: [], views: [], models: [] };
var dirs = ['controllers', 'views', 'models'];

dirs.forEach(function(dirname) {
    fs.readdir('./' + dirname, function(err, res) {
        if (err) throw err;
        result[dirname] = res;
        // #2
    });
});

// #1

在这段代码中,console.log(result);运行在#1(见上文),将记录初始化的空控制器,视图和模型数组。但是,我需要循环来填充数组,并通过fs读取相应的文件名。

#p> console.log(result);#2将在第三次迭代后记录填充了所需值的result对象。

我认为这与Node.js / JavaScript回调的异步性质有关。如果我不理解JavaScript变量范围和异步方法是如何工作的,请原谅我,我对此都是新手。

5 个答案:

答案 0 :(得分:2)

这样做:

var result = { controllers: [], views: [], models: [] };
var dirs = ['controllers', 'views', 'models'];
var pending = 0;

dirs.forEach(function(dirname) {
    pending++;
    fs.readdir('./' + dirname, function(err, res) {
        pending--;
        if (err) throw err;
        result[dirname] = res;
        if (pending===0) goOn();
    });
});
function goOn() {
    // #1
}

答案 1 :(得分:2)

  

我认为这与异步性质有关   Node.js / JavaScript回调。

是的,这可能就是为什么当您尝试在#1输出result变量的内容时,它是空的。在#1运行时,数据尚未被提取,因为“提取”动作发生在#{1}}的回调#2处。我建议查看本answer中所述的有关异步范例的一些资源,以便更好/更好地了解回调和异步编程的工作原理。

答案 2 :(得分:0)

对于对措辞感到困惑的人,这里是每个目录中带有'1'文件的示例的输出:

~/Documents/$ node test.js 
{ controllers: [], views: [], models: [] } 1
{ controllers: [], views: [ '1' ], models: [] } 2
{ controllers: [ '1' ], views: [ '1' ], models: [] } 2
{ controllers: [ '1' ],
  views: [ '1' ],
  models: [ '1' ] } 2

这是因为进入文件系统需要进行异步操作。因此,由于Node不会阻止值,因此将与异步操作完成之前相同。

答案 3 :(得分:0)

它确实与回调有关。在

fs.readdir('./' + dirname, function(err, res) {
    if (err) throw err;
    result[dirname] = res;
    // #2
});

传入的函数是一个回调函数,它在完全读取目录时执行。由于它是异步的,因此fs.readdir()在函数实际执行之前返回

所以基本上,在你的forEach完成之后,你有3个函数等待作为回调执行(每个目录一个)。代码不会在继续执行之前等待回调发生,所以如果在读取目录之前达到#1,它将在回调执行之前记录结果对象并适当地修改它。

您可以改为使用fs.readdirSync,但是如果此代码短暂阻止,则不会对您的应用程序造成不利影响/此代码无法无限期阻塞并阻止您的程序。如果你需要它保持异步,请查看你的答案。

答案 4 :(得分:-1)

我相信,如果dirname在第一个函数返回后发生变化,那么该值就是这样做的。要解决这个问题,只需将dirname复制到第二个函数调用的范围内;

dirs.forEach(function(dirn) {
    var dirname = dirn;  // make a copy here
    fs.readdir('./' + dirname, function(err, res) {
        if (err) throw err;
        result[dirname] = res;
        // #2
    });
});