react ajax : multiple nested fetch Get

2019-05-10 18:14发布

问题:

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.

回答1:

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.
                  });
                });
              });
            })
      }


回答2:

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);
        })
      })
    });
}