1

I have arrays like below.

var clients = ['a','b'];
var reports = ['x','y','z'];
var finalData = [];

Now i need looping like this.

for(var i=0;i<reports.length;i++){
   var response = {report : reports[i]}
   for(var j=0;j<clients;j++){
       response.client = clients[i];
      $.ajax({
        url :url,
        success : function(data){
          response.data = data;
          finalData.push(response);
        })
     });
   }
}

As ajax is asynchronous so it does not work properly. Also I need to wrap this up into react.js componentDidMount function and push the finalData into the state.

I tried $.each instead of for loop and .bind(this) to push the finaldata into the state. but i didnt get the correct data. I heard for async calls closure should be used but not getting how will that be implemented here. The output i am looking for is.

finalData = [
        {client:a,report:x,data : 'xyz'},
        {client:b,report:x,data : 'xyz'},
        {client:a,report:y,data : 'xyz'},
        {client:b,report:y,data : 'xyz'},
        {client:a,report:z,data : 'xyz'},
        {client:b,report:z,data : 'xyz'}
     ]
Rahul Tailwal
  • 3,183
  • 3
  • 14
  • 27
  • 1
    You could use closures to preserve the reports and clients value see this for more detail http://stackoverflow.com/questions/6077357/passing-index-from-for-loop-to-ajax-callback-function-javascript – Vinod Louis Jan 19 '17 at 11:43
  • You have multiple coding errors in your example code. e.g. `clients[i]` should be `clients[j]`. – Igor Skoric Jan 19 '17 at 12:41

1 Answers1

2

You have to save the scope(use colsure) for your response variable, try the following:

for(var i=0;i<reports.length;i++){
   var response = {report : reports[i]}
   for(var j=0;j<clients;j++){
      response.client = clients[i];

      (function(responce){
      $.ajax({
        url :url,
        success : function(data){
          response.data = data;
          finalData.push(response);
        })
      })(responce);

     });
   }
}

let's explain it more, to simulate ajax will use setTimeout here in the example:

let's consider this example, its a loop 5 times and after a sec will need to print i we are expecting to print 0 1 2 3 4 but actually it will print 5 for the 5 times because the for loop end before the 1 sec consume and then when it search for i to print i in this case = 5

for(var i=0;i<5;i++){
  setTimeout(function(){
    console.log(i);
    console.log("....");
  }, 1000) 
}

and to solve that will need to have a scope to save the value for i so when the timeout finish it can find the correct value for i which is called closure:

for(var i=0;i<5;i++){
  (function(i){
    setTimeout(function(){
      console.log(i);
      console.log("....");
    }, 1000)
  })(i);
}

this code will work as expected will print 0 1 2 3 4 correctly and that is what have done for your example.

and for updating the state you can either update it each time you receive a value as:

var that = this; // to be added in top of the 2 for loops
finalData.push(response);
this.setState({data: finalData})

or you can check if the all reports loaded for all clients and then update the state:

// total can be calculated by num of reports X number of clients
if(finalData.length == total){
  this.setState({data: finalData})
}
mohamed-ibrahim
  • 10,837
  • 4
  • 39
  • 51