There is a function that first gets all zones in an environment and then when he found these zones for each zone it will do an api call for the borderpoints.
All fetches get executed but the problem is that in the then for the first fetch if I try to write out the length of the zones or try to do a .foreach I get 0.
I know this is because it runs async and it is not yet loaded on the moment I do the console.log but I tought it should be. That's why we use the .then isn't it? to wait until the data has been loaded.
To be clear: I'm looking for a way to make zones.length give back the real length of the array of objects and be evaluated immediately instead of 0. I can't find out how:
Here is the code.
getZones(){
var that = this;
var zones = new Array();
fetch('api/environment/zone/1').then(function(response){
response.json().then(function(data){
data.forEach(function(element){
zones[element.zoneID] = element;
zones[element.zoneID].points = [];
fetch('api/zone/point/'+element.zoneID).then(function(response){
response.json().then(function(data){
data.forEach(function(element){
zones[element.zoneID].points.push(element);
});
}); //closing of data inner fetch.
});
});
});
}).then(function(response){
console.log(zones); //this gives all zones
console.log(zones.length); //this one gives 0
//the for each is never executed because it's still 0.
zones.forEach(function(element){
console.log(element);
});
});
}
Already a big thanks for the fast Reaction and help.
The calls to fetch
returns an Promise
and that is async
. So your second .then
executes before even the code inside the first then completes because, your first then contains
another fetch
function being called which is async. You can have a logic like below to wait until you get all points data and the proceed with your logic.
getZones(){
var that = this;
// var zones = new Array();
// this function gets called finally.
// Do what you want to do with zones and points here.
var gotAllZones = function(zones) {
console.log(zones); //this gives all zones
console.log(Object.keys(zones).length); //this one gives 0
Object.keys(zones).forEach(function(index){
console.log(zones[index]);
});
}
fetch('api/environment/zone/1').then(function(response){
response.json().then(function(data){
//decleare zones to be a object. Based on your code `zones` is not an array
var zones = {};
var pointsDone = 0; // declare how many points api has been completed. Initially its `0`
data.forEach(function(element){
zones[element.zoneID] = element;
zones[element.zoneID].points = [];
fetch('api/zone/point/'+element.zoneID).then(function(response){
response.json().then(function(innerData){
innerData.forEach(function(element){
zones[element.zoneID].points.push(element);
});
pointsDone++; //each time we are done with one. we increment the value.
// we check if we are done collecting all points data
// if so we can call the final function.
if(pointsDone === data.length) {
gotAllZones(zones);
}
}); //closing of data inner fetch.
});
});
});
})
}
In the end I fixed it in a more beautiful way:
getZones2(){
var that = this;
var zones = [];
fetch('api/environment/zone/1')
.then(res => res.json())
.then((json) => {
Promise.all(
json.map(
element => fetch('api/zone/point/' + element.zoneID)
.then(res => res.json())
)
).then(datas => {
json.forEach((element, i) => {
zones[element.zoneID] = element
zones[element.zoneID].points = datas[i]
})
console.log(zones);
zones.forEach(function(response){
that.makeZone(response.name,response.zoneID,response.points);
})
})
});
}