【问题标题】:Node.js: problem with variable scope in callbackNode.js:回调中变量范围的问题
【发布时间】:2011-08-06 21:39:59
【问题描述】:
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

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

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

我相信这与 Node.js / JavaScript 回调的异步特性有关。如果我不了解 JavaScript 变量作用域和异步方法的工作原理,请原谅我,我是新手。

【问题讨论】:

    标签: javascript node.js callback


    【解决方案1】:

    这样做:

    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
    }
    

    【讨论】:

    • 目前看起来不错。我并不完全相信,因为这个解决方案包含了嵌套——不过,这是一个可行且合理的解决方案。干杯!
    • @thejh pending 在回调中具有正确值的保证是什么?
    【解决方案2】:

    我相信这与 Node.js / JavaScript 回调。

    是的,这可能是为什么当您尝试在#1 处输出result 变量的内容时,它是空的。在#1 处运行时,根本还没有获取数据,因为“获取”操作发生在#2 处readdir 的回调执行过程中。我建议查看answer 中所述的一些有关异步范例的资源,以便更全面地了解回调和异步编程的工作原理。

    【讨论】:

    • 小心。并非所有回调都将始终异步执行。
    • 谢谢,我阅读了所有“Node.js 中的控制流”系列。不错的资源!
    【解决方案3】:

    对于那些对措辞感到困惑的人,以下是每个目录中都有一个“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 不会阻塞,因此之后的值将与异步操作完成之前相同。

    【讨论】:

    • 这是完全合理的,但使用fs.readdirSync() 也不起作用。
    【解决方案4】:

    它确实与回调有关。在

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

    传入的函数是一个回调,当目录被完全读取时执行。因为它是异步的,所以 fs.readdir() 返回 before 函数实际执行。

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

    您可以改用 fs.readdirSync,但仅当此代码短暂阻塞时对您的应用程序没有坏处/没有此代码无限期阻塞和停止程序的危险。如果您需要它保持异步,请查看 jh 的答案。

    【讨论】:

    • 不要 +1,因为你真的应该警告 fs.readdirSync 在很多情况下是个坏主意。
    【解决方案5】:

    我相信如果 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
        });
    });
    

    【讨论】:

      猜你喜欢
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-12
      • 1970-01-01
      • 2015-06-14
      • 1970-01-01
      • 2016-12-18
      相关资源
      最近更新 更多