9
jQuery: Using .ajax() inside a loop and variable scope
26 Comments · Posted by Darian in Development
On my latest web app I ran into a problem that had me stumped for a while. I was trying to load data via AJAX from a number of files to insert into the web page. I did this by iterating through a number of jQuery .ajax() calls with a for loop. I was getting very strange behaviour such as seemingly missing AJAX calls and a muddle for-loop! It took a lunch break to clear my mind so I could regather my wits and troubleshoot the issue. Which turned out to be quite a sly JavaScript variable scope problem.
Here’s an example of my problem code (edited for simplicity). You can see the ‘i‘ variable is used to point each requested file (i.e. ‘panel_data_0.xml‘) and to then place the response data in the HTML element (i.e. ‘#panel-0‘). This way each XML file’s data is inserted into its numbered HTML element, five all up, numbered 0 to 4:
[code lang="js"]function loadPanelData()
{
// Iterate through our 5 panels...
for (i = 0; i <= 4; i ++)
{
$.ajax(
{
type: 'GET',
url: 'panel_data_' + i + '.xml',
success: function(xml)
{
$(xml).find('data').each(function()
{
// Read data from XML...
var heading = $(this).find('heading').text();
var paragraph = $(this).find('paragraph').text();
// Insert data into panel...
$('#panel-' + i).append('<h1>' + heading + '</h1>');
$('#panel-' + i).append('<p>' + paragraph + '</p>');
});
},
error: function(xml)
{
// Handle errors here.
}
});
}
}[/code]
The problem came about because the AJAX call is asynchronous meaning the for-loop is allowed to iterate from 0 to 4 without waiting for the AJAX request to receive its response from the server. That’s all well and good, until I realised that I’m relying on the ‘i’ variable in the .ajax() success callback function, and by the time that is reached, i has already zipped through the loop up to 4. Things get messy from there! :-S
The cheap and nasty fix
The most obvious answer is to simply add the ‘async‘ option and set it to false – like so:
[code lang="js" highlight="5"]$.ajax(
{
type: 'GET',
url: 'panel_data_' + i + '.xml',
async: false,
success: function(xml)
{
// ... etc., etc...[/code]
This makes our AJAX synchronous, so that the for-loop must wait for the jQuery .ajax() function to complete before being allowed to continue on. But then our page will load slowly in stages and the browser will be interrupted. Basically, it’s an ugly fix.
The elegant fix
The better way to go about solving this problem is to give each .ajax() function its own copy of variable ‘i‘ so that no matter what the for-loop does, we always know what ‘i’ used to be and should be for each AJAX call. This allows us to keep the AJAX calls in the for-loop, and have all the AJAX calls happen asynchronously, at the same time!
Here’s how it looks:
[code lang="js" highlight="10, 13"]function loadPanelData()
{
// Iterate through our 5 panels...
for (i = 0; i <= 4; i ++)
{
$.ajax(
{
type: 'GET',
url: 'panel_data_' + i + '.xml',
ajaxI: i, // Capture the current value of 'i'.
success: function(xml)
{
i = this.ajaxI; // Reinstate the correct value for 'i'.
$(xml).find('data').each(function()
{
// Read data from XML...
var heading = $(this).find('heading').text();
var paragraph = $(this).find('paragraph').text();
// Insert data into panel...
$('#panel-' + i).append('<h1>' + heading + '</h1>');
$('#panel-' + i).append('<p>' + paragraph + '</p>');
});
},
error: function(xml)
{
// Handle errors here.
}
});
}
}[/code]
You can see I added ‘ajaxI’ in the .ajax() function options and set it to ‘i’. This means that as soon as we create the .ajax() function we preserve the value of ‘i’. Then when we finally get a response back and before we insert the data into the HTML, I reinstate the value of ‘i’ so everything matches up nicely.
AJAX · development · hints · JavaScript · jQuery · tips · variable

Ryan Wheale · 9 November 2010 at 9:48 am
Or you could use the built in “context” property of the ajax object.
Admin comment by Darian · 9 November 2010 at 11:06 am
Great point Ryan, I hadn’t looked at context in this way. From what I understand, context is a way of limiting your jQuery selector to a certain DOM node.
I haven’t used context before, but I’m guessing you mean when the .ajax() call is made I should add an option like this?…
$.ajax({
type: 'GET',
url: 'panel_data_' + i + '.xml',
context: $('#panel-' + i),
success: function(xml)
{
// ... etc, etc,...
And then in the success callback, do something like this:
// Insert data into panel...$(this).append('<h1>' + heading + '</h1>');
$(this).append('<p>' + paragraph + '</p>');
I’ll play around with this
later todaynext week. It’s looks like my post may need to be updated! Thanks for the feedback.amirali shahinpour · 2 July 2011 at 2:53 pm
you are awsome i just make my app work very good with turn true to false like this
xmlhttp.open(“GET”,”find_graph.php?who=”+$name[$j],false);
before that is was
xmlhttp.open(“GET”,”find_graph.php?who=”+$name[$j],true);
thanks
ata · 29 December 2010 at 1:37 am
Thank you !!!
Amy · 1 July 2011 at 8:29 am
THANK YOU! This is exactly what I was looking for!!!!!
Amin · 22 October 2011 at 10:05 am
THANK YOU, this is my problem exactly.
Milouse · 29 October 2011 at 8:40 am
You saved my day, thanks!
Passing variables through the ajax request for success callback is something i didn’t even try before.
Hudson · 9 December 2011 at 10:48 am
THANKS!!!!!! that did the trick. have a beer.
.kkursor · 15 March 2012 at 8:23 pm
Thank you very much, you saved my brain!
Pawan Choudhary · 16 May 2012 at 8:52 pm
THANKS A LOT, IT WAS GREAT
Satish Kumar · 7 June 2012 at 1:07 am
Thanks a lot .. But I just had the same issue, for which I used async=false.
It worked!
chatoxz · 4 July 2012 at 8:39 am
gracias mandril!
Stranger · 13 July 2012 at 9:54 pm
not a very interested article.I want Bind my Result which return from success function which is a List to a div On success function..Can u help me Bro..
Sascha · 27 August 2012 at 12:10 am
Thanks Darian for the comprehensive explanation! I had used the $.ajax “context” and it got overwritten when my plugin doing the ajax call was called multiple times; adding my options array as a variable to the ajax call solved the problem!
Regards, Sascha
ajax jquery loop iteration « InterestsMe · 31 August 2012 at 11:48 pm
[...] for and what it meant, but never thought it’ll be this important till I got reinstated by this blog, which was addressing same problem and eventually put me on the right track. So thank you, you [...]
Andrea Porotti · 23 October 2012 at 8:03 pm
You are my Hero
Used ‘elegant fix’… Thanks a lot!
Brian · 25 October 2012 at 10:08 am
Thanks for this post, I was having this exact problem with an object instead of a number. Your elegant fix did the trick!
3 Roads Media · 7 January 2013 at 7:36 am
You are absolutely the man, and saved me a ton of frustration. This is just what I was looking for! I wasn’t able to figure out how to do this using the shorthand $.get() function, but ah well. Thanks!
chrhlnd · 13 January 2013 at 6:52 pm
Just wrap your processing in another function and pass i to it. It will capture at the current value. Into another function context. No need to muck with the ajax object.
$.ajax(
{
type: ‘GET’,
url: ‘panel_data_’ + i + ‘.xml’,
success: function(xml)
{
//magic function context
function (idx) {
$(xml).find(‘data’).each(function()
{
// Read data from XML…
var heading = $(this).find(‘heading’).text();
var paragraph = $(this).find(‘paragraph’).text();
// Insert data into panel…
$(‘#panel-’ + idx).append(” + heading + ”);
$(‘#panel-’ + idx).append(” + paragraph + ”);
}(i) // pass the value here.. also call the anon function
});
},
chrhlnd · 13 January 2013 at 6:55 pm
Oops you do have to capture it earlier.. because success won’t be called till later
make anon
function (idx) {
$.ajax(
{
type: ‘GET’,
url: ‘panel_data_’ + idx + ‘.xml’,
success: function(xml)
{
$(xml).find(‘data’).each(function()
{
// Read data from XML…
var heading = $(this).find(‘heading’).text();
var paragraph = $(this).find(‘paragraph’).text();
// Insert data into panel…
$(‘#panel-’ + idx).append(” + heading + ”);
$(‘#panel-’ + idx).append(” + paragraph + ”);
});
})(i) // call anon,
Chexpir · 19 February 2013 at 7:44 pm
Thank you very much! That saves me a lot of time. As my case depends on the previous one on the loop, ‘async: false,’ was the code I need
Disha · 16 April 2013 at 11:01 pm
same case.
Disha · 16 April 2013 at 11:00 pm
you are life savior ! I was looking for it since a week and your post solved the problem ! good job.
Bogdan · 2 May 2013 at 8:50 pm
Just to add another thanks for posting this, saved me from a lot of frustration after some endless googling about running something after waiting on a batch of ajax calls to run and complete inside a loop. Great work!
Ajax serialize form with conditions (for php). How to pass only necessary data with ajax? | BlogoSfera · 12 May 2013 at 4:03 am
[...] this link http://www.dariancabot.com/2010/11/09/jquery-using-ajax-inside-a-loop-and-variable-scope/… may be this would be [...]
free shooting targets · 12 May 2013 at 6:06 am
Link exchange is nothing else but it is only placing the other person’s web site link on your page at proper place and other person will also do same in favor of you.