ILE ILE - 1 month ago 13
jQuery Question

Store ajax response in outside array

I know many topics on this subject already exist (eg How do I return the response from an asynchronous call?) but currently I'm in a state of information overload and I can't seem to get it done. Many topics say to use synchronous requests but since this is not desirable (and by the way not supported anymore by my browser) so I must avoid this.

Problem:

I have a list. I want to pass each item to an ajax request to collect some additional information. The result should be a new list with the additional data.

var list=['a','b','c','d'];
var newlist=[];

for(element in list)
{
var item=list[element];
$.ajax({
url: "process.php",
type:"get",
data:{content:item}
}).success(function(response)
{ newlist.push([item,response]);}
}};
}


I figured now I should have a list like

newlist=[ ['a','response'],['b','response'],['c','response'],['d','response'] ];


(with 'response' being the actual response of course). However, this is not the case. When I place an alert in the success function to display the item it shows 'd' four times.

What am I doing wrong?

Answer

Actually, you're having a closure problem (there are hundreds of posts in SO that address it). You can check this post in order to get a full explanation (or find a good article on the internet).

In essence, the problem stems from the fact that the for is synchronous, whilst the ajax calls are asynchronous. This means that your for loop will finish before any ajax call will do. The callbacks of the Ajax wrap over item, and item will be d for all of them (therefore the four d).

A quick fix would be to use a forEach instead. To prove it, your problem can be simulated using another aynchronous function instead of your ajax call:

var list = ['a', 'b', 'c', 'd'];
var newlist = [];

// Closure problem
for (element in list) {
  var item = list[element];
  setTimeout(() => console.log(item), 0);
}

// No closure problem
list.forEach((item) => setTimeout(() => console.log(item), 0));

So, applying this to your code, I think your problem could be solved as follows:

var list = ['a', 'b', 'c', 'd'];
var newlist = [];

list.forEach(function(item) {
  $.ajax({
    url: "process.php",
    type: "get",
    data: {
      content: item
    }
  }).success(function(response) {
      newlist.push([item, response]);
    }
  }
});

Hope it helps.

Comments