在纯JavaScript中,如何加载两个JSON文件并将它们作为命名变量传递?

时间:2016-12-10 20:24:32

标签: javascript json asynchronous

我确信这是Javascript中经典的初学者错误 - 绊倒异步回调。我想读取一些JSON文件,然后将内容解析为嵌套层次结构。所以:从每个中加载JSON数据,将每个数据分配给一个对象,然后将这两个对象作为参数传递给一个组合它们的函数。

在Python中,我可以这样做:

with open('data/vpcs.json') as data_file:
  vpc_data = json.load(data_file)
with open('data/subnets.json') as data_file:
  subnet_data = json.load(data_file)
do_stuff(vpc_data, subnet_data)

在Javascript中,我有一个函数发出XHR请求,然后在完成请求后给我一个回调函数。这一点有效。

我正在处理的这个部分是一种调用两次的方法,在尝试对两个数据对象执行任何逻辑之前,等待我的两个文件的内容安全返回。下面是:

tree = parse('data/vpcs.json', 'data/subnets.json')

function loadJSON(file, callback) {   
  // makes an XHR request using the code sample here which hands the response back as a callback:  
  // https://codepen.io/KryptoniteDove/post/load-json-file-locally-using-pure-javascript 
}

function parse(vpcs, subnets) {
  loadJSON(vpcs, function(response) {
  // Parse JSON string into object
    var vpc_data = JSON.parse(response).Vpcs;
    console.log(vpc_data); // this works
  });
  loadJSON(subnets, function(response) {
    // Parse JSON string into object
    var subnet_data = JSON.parse(response).Subnets;
    console.log(subnet_data); // this works
  });
  toHierarchy(vpc_data, subnet_data);
  // oh no! this doesn't work! 
  // my variables are outside the callback, and still undefined!
}

function toHierarchy(vpcs, subnets) {
// this poor sad function never gets any JSON.
}

我应该在回调中调用我的toHierarchy函数 - 这是确保我的变量完全填充的机制。但是我不明白这里有什么适当的回调使用 - 我知道有很多'回调地狱'反模式我应该避免!

据推测,我可以将一个回调放在另一个回调中,或者执行类似这样的回答 - https://stackoverflow.com/a/28690291/3276583 - 循环等待回调完成并将响应推送到数组中。但我不需要按顺序加载这些文件,只是为了能够将它们设置为数组中的命名键。

干净的方法是什么?我可以编写一个回调,它是一个命名函数而不是一个匿名函数,并使其成为通用:给它一个文件和我想将其内容设置为的变量的名称?

编辑:

从@Bradley Bossard的建议下线,我试过:

function parse(vpcs, subnets) {
  var vpc_data = null
  var subnet_data = null
  loadJSON(vpcs, function(response) {
    // Parse JSON string into object
    var vpc_data = JSON.parse(response).Vpcs;
    console.log("In the vpc callback, subnets are: " + subnet_data);   
    tree = toHierarchy(vpc_data, subnet_data);
    console.log(tree);
  });
  loadJSON(subnets, function(response) {
    // Parse JSON string into object
    var subnet_data = JSON.parse(response).Subnets;
    console.log("In subnet callback, vpcs are: " + vpc_data);
    tree = toHierarchy(vpc_data, subnet_data);
    console.log(tree);
  });

  tree = toHierarchy(vpc_data, subnet_data);

  function toHierarchy(vpcs, subnets) {
    if (vpcs === null || subnets === null) {
      return "toHierarchy function returns because things are null :("
    }

但我似乎无法从另一个回调中访问该变量:

   hierarchise.js:38 In the vpc callback, subnets are: null
   hierarchise.js:40 toHierarchy function returns because things are null :(
   hierarchise.js:45 In subnet callback, vpcs are: null
   hierarchise.js:47 toHierarchy function returns because things are null :( 

2 个答案:

答案 0 :(得分:2)

正如Bergi所说,承诺是要走的路,因为它允许你避免那些嵌套的回调。

您需要调整loadJson函数以返回承诺,而不是接受回调函数。这样控制就会返回给调用者。

function loadJson(file) {
    return new Promise(function(resolve, reject) {
      var request = new XMLHttpRequest();
      request.open('GET', file);
      request.responseType = 'json';
      request.onload = function() {
        if (request.status === 200) {
          resolve(request.response);
        } else {
          reject(Error('Didn\'t load successfully; error code:' + request.statusText));
        }
      };
      request.onerror = function() {
          reject(Error('There was a network error.'));
      };
      request.send();
    });
}

然后,您可以使用Promise.all函数等待所有请求完成。

tree = parse('data/vpcs.json', 'data/subnets.json');

function parse(vpcs, subnets) {
    Promise.all([loadJson(vpcs), loadJson(subnets)]).then(function(responses) {
        // responses contains the parsed JSON objects in the order of requests
        toHierarchy(responses[0], responses[1]);
    }).catch(function(error) {
        // do error processing here if any promise was rejected
    });
}

function toHierarchy(vpcs, subnets) {
    // do the combination here
    console.log(vpcs);
    console.log(subnets);
}

答案 1 :(得分:0)

免责声明:我没有运行此代码。

我认为你想要做的是在每个loadJSON回调函数中调用toHeracrchy,然后测试每个参数以验证它们是否有效。如果没有,则返回,因为一旦其他JSON到达,将再次调用该函数。 B / c loadJSON是异步的,你不能保证哪一块JSON会先到达,所以你应该只检查一下它们是否存在。

tree = parse('data/vpcs.json', 'data/subnets.json')

function loadJSON(file, callback) {   
  // makes an XHR request using the code sample here which hands the response back as a callback:  
  // https://codepen.io/KryptoniteDove/post/load-json-file-locally-using-pure-javascript 
}

function parse(vpcs, subnets) {
  var vpc_data = null;
  var subnet_data = null;
  loadJSON(vpcs, function(response) {
  // Parse JSON string into object
    vpc_data = JSON.parse(response).Vpcs;
    console.log(vpc_data); // this works
    toHierarchy(vpc_data, subnet_data);
  });
  loadJSON(subnets, function(response) {
    // Parse JSON string into object
    subnet_data = JSON.parse(response).Subnets;
    console.log(subnet_data); // this works
    toHierarchy(vpc_data, subnet_data);
  });

  function toHierarchy(vpcs, subnets) {
    if (vpcs === null || subnets === null) {
      return;
    }
    // execute only when vpcs and subnets have valid data.
  }
}