javascript - Javascript 中嵌套函数 "Class"结构中的变量范围

标签 javascript node.js asynchronous scope

出于各种原因,我正在将 Python 应用程序移植到 Node.js。我通过 Web 开发获得了一定的 Javascript 知识,但在变量范围和(可能?)我正在进行的一些异步调用方面遇到了一些问题。

因此,我的 DataLoader 类中有一个嵌套方法“xmlToObjectByType”,我试图根据一系列 XML 文件中的某些匹配条件在其中设置变量。无论我在方法中做什么,产品都不会从 null 改变,并且 xml_files.splice() 调用永远不会起作用。我确信这是一个范围问题(在 fs.readFile 内部的 parseString 中的代码,在 forEach 等内部) 但我还没有找到太多的运气来弄清楚到底为什么或如何正确获取设置的值。

作为最后的努力,我尝试在 xmlToObjectByType 上设置的回调中获取结果,这为我提供了我正在寻找的值,但我仍然无法从打回来。我确信这与范围界定有关,但我有点不知所措。我确信这是一件我忽略的非常简单的事情,但我已经很长时间没有深入研究 JS 了。这根本不是很深。除了可能糟糕的逻辑流程之外,您对我在这里做错了什么有什么想法吗?

请注意,这是一个简化版本,出于易读性原因,我在其中删除了对其他一些 XML 文件类型的检查。

代码

var fs = require('fs'),
    xml2js = require('xml2js');


export function DataLoader(working_directory){  
  var working_directory = working_directory;
  var xml_files = [];
  var products = null;
  var data = null;

  var xmlToObjectByType = function(type, setValue) {
    xml_files.forEach(function(file, index) {
        var parser = new xml2js.Parser();
        fs.readFile(working_directory + '/' + file, function(err, data) {
            parser.parseString(data, function (err, result) {
                if (result.Products.Product) {
                    var result_object = result.Products.Product;
                    // check if we've got at least one row, else return false
                    if (result_object.length > 0) {

                        // products specific check
                        if (type == "products") {
                            // identify products XML with artist tag
                            if (result_object[0].Artist) {
                                // this is a products XML file, so pop this file from xml_files, return object
                                xml_files.splice(index, 1);
                                setValue(result_object);
                            } 
                        }

                    } else {
                        // no rows in object
                        setValue("no rows");
                    }
                } else {
                    // ROW object isn't set, malformed XML
                    setValue("malformed XML");
                }
            });
        });
    })
  }

  // check selected directory for XML files
    fs.readdir(working_directory,function(err,files){
        if(err) throw err;
        files.forEach(function(file){
            // do something with each file HERE!
            if (file.split('.').pop() == "xml") {
                xml_files.push(file);
            }
        });

            // if they don't exist return and send message
            if (xml_files.length < 1) {
                var status = {status: "error", message: "There are no XML files in the directory you selected."};
            } else {

                // process further
                xmlToObjectByType("products", function(result) {
                    products = result;
                });


                data = {"products": products};

                // products always has the value null here
                console.log(data);
            }

            return status;
     });
};

我通过以下方式调用它

import { DataLoader } from './my_module';
DataLoader('/Path/To/XML');

还有一个 XML 文件的简化示例(我认为我做对了)

<?xml version="1.0" encoding="UTF-8" ?>
<Products>
    <Product>
        <Artist>Test</Artist>
        <Title>Test Title</Title>
        <Description>Maecenas faucibus mollis interdum. Donec sed odio dui. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Aenean lacinia bibendum nulla sed consectetur. Cras mattis consectetur purus sit amet fermentum.</Description>
    </Product>
    <Product>
        <Artist>Test</Artist>
        <Title>Test Title</Title>
        <Description>Maecenas faucibus mollis interdum. Donec sed odio dui. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Aenean lacinia bibendum nulla sed consectetur. Cras mattis consectetur purus sit amet fermentum.</Description>
    </Product>
    <Product>
        <Artist>Test</Artist>
        <Title>Test Title</Title>
        <Description>Maecenas faucibus mollis interdum. Donec sed odio dui. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Aenean lacinia bibendum nulla sed consectetur. Cras mattis consectetur purus sit amet fermentum.</Description>
    </Product>
    <Product>
        <Artist>Test</Artist>
        <Title>Test Title</Title>
        <Description>Maecenas faucibus mollis interdum. Donec sed odio dui. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Aenean lacinia bibendum nulla sed consectetur. Cras mattis consectetur purus sit amet fermentum.</Description>
    </Product>
</Products>

最佳答案

Macmee 是正确的,因为您的 dataproduct 变量仅在异步调用的 xmlToObjectByType 回调范围内更改(在您请求将结果记录到控制台之后,处理器的下一个滴答声)。

但我认为您的主要问题不在于您对数据的处理,而在于您在代码中请求有关数据的反馈的时间点。

因此,在您当前的代码中,products 正在使用表示 XML 的对象进行填充,但它只是在您要求打印之后很久才进行填充回到你身边。您可以通过放置一个简单的定时函数来测试这个理论,该函数将在半秒内报告结果:

/* ... code before */

var working_directory = working_directory;
var xml_files = [];
var products = null;
var data = null;

setTimeout(function(){

  console.log(products);

}, 500);

/* code after... */

因此,您真正想要的是一般情况下 DataLoader回调:

function DataLoader(working_directory, callback){ 

并在 XML 解析回调中调用它:

xmlToObjectByType("products", function(result) {
    callback({"products": result});
});

然后像这样调用您的DataLoader:

DataLoader('/Path/To/XML', function(data){ console.log(data) });

通过对代码进行这些调整,我在控制台中得到以下输出:

{ products: 
   [ { Artist: [Object], Title: [Object], Description: [Object] },
     { Artist: [Object], Title: [Object], Description: [Object] },
     { Artist: [Object], Title: [Object], Description: [Object] },
     { Artist: [Object], Title: [Object], Description: [Object] }
   ]
}

显然我正在对您的用户案例做出一些假设。但我认为这演示了如何在异步 JavaScript 中导航不同的范围。

更新

下面是对应用程序的重写,它将整理多个 XML 文件中的数据,然后使用数据进行回调:

var fs = require('fs'),
    xml2js = require('xml2js'),
    path = require('path');


export function DataLoader (directory, callback) {  

  getXmlFiles( function (files) {

    parseXmlFile(files, callback);

  } );

  function parseXmlFile (files, callback) {

    var parser = new xml2js.Parser();
    var data = {};
    var filesLeft = files.length;

    files.forEach( function(file, i) {

      fs.readFile( path.join(directory, file), function (err, result) {

        parser.parseString( result, function (err, result) {

          if (result.Products.Product) {

            var result_object = result.Products.Product;

            if (result_object.length > 0 && result_object[0].Artist) {

              data[file] = result_object;

            }
          }

          filesLeft--;

          if (!filesLeft)
            callback(data);

        });
      });
    });
  }

  function getXmlFiles (callback) {

    var files = [];

    fs.readdir(directory, function (err, f) {

      if(err) throw err;

      f.forEach( function (file) {

        if (file.split('.').pop() == "xml") {

          files.push(file);

        }
      });

      callback(files);

    });
  }
}

将它与这样的东西一起使用:

DataLoader( __dirname, function (data) {

  console.log(data);

});

关于javascript - Javascript 中嵌套函数 "Class"结构中的变量范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33769595/

相关文章:

javascript - wait 仅在异步函数的异步函数错误中有效

node.js - 为什么带有 SparkPost 的电子邮件会成为垃圾邮件?

python - 使用用户定义或 python 命令序列的 C++ 线程不会在 gdb 异步模式下停止

c# - 在 Action 中使用带有 await 的 ForEachAsync 时不等待

javascript - 为什么 React.Component 的内部实现是一个函数而不是 ES6 类?

javascript - JavaScript 中的正则表达式用于处理 URL

javascript - 检查Javascript中是否存在事件超时

javascript - 如何检测容器何时失去焦点

javascript - Node +回调函数传参

jquery - 同步jquery $.ajax而不锁定IE?