简体   繁体   中英

Javascript closure for asynchronous ajax handling on for loops

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'}
     ]

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM