我确信这是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 :(
答案 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.
}
}