I am a beginner in Javascript and I feel that there is something wrong with me about the $.get jQuery.
Normally, you can assign it to a function that will execute after the data is retrieved correctly.
But if I put my $.get in a loop, the loop continues to execute even if the data is not yet retrieved, and here is my problem.
Here is my code (this is for GreaseMonkey):
var1 = document.getElementsByClassName("some_class");
i = 0;
while (i < var1.length) {
url = var1[i].getElementsByTagName("some_tag")[0].href;
$.get(url, function(data) {
if (data.contains("some_string")) {
alert(i);
}
});
i++;
}
Here, the alert returns var1.length event if it should returns 1 for exemple.
I try to put an alert(i) just after the url declaration and I understood that i++ was done before the function in my $.get.
This is surely a trivial problem, but I can not grasp the logic to not make this happen.
Here's a little demo for you to investigate further.
Sample
async=true
output:Sample
async=false
output (and the browser hangs for 5558ms!):Since you're using jQuery, you can simplify your code quite a bit:
Changes from the original code are:
getElementsBy*
functions..each()
for the loop.var
where needed. (Very important in any version of the code!)Note that the use of
.each()
automatically gives you the same effect as the immediately invoked function expression (IIFE) in another answer, but without the extra complication. That's because.each()
always uses a callback function, and that creates the closure needed to preserve thei
variable (andelement
too) uniquely for each iteration of the loop.You can also do this when you have an ordinary
while
orfor
loop, and you still don't need the IIFE. Instead, simply call a function in the loop. Written this way, the code would be:As you can see, the
checkElement
function is identical to the.each()
callback function. In fact,.each()
simply runs a similarfor
loop for you and calls the callback in exactly the same way as this code. Also, thefor
loop is more readable than thewhile
loop because it puts all the loop variable manipulation in one place. (If you're not familiar with thefor
loop syntax it may seem less readable at first, but once you get used to it you will probably find that you prefer thefor
loop.)In general, when tempted to use an IIFE in the middle of a loop, try breaking that code out into a completely separate function instead. In many cases it leads to more readable code.
Wrap your
$.get
function thus:The immediately invoked function expression causes the current value of
i
that's in the outer scope to be bound via the function's parameteri
(which then hides the outer variable). If you like, give the function parameter a different name.Note that this only fixes the problem you actually stated, which is that the loop variable is incremented independently of the callbacks. If you wish to ensure that the AJAX requests run one at a time then there are other solutions, e.g.:
The
.done()
call is in the form.done(callback, loop)
and the two functions will be called in order. So thei++
line always happens first, and then it arranges forloop
to be called pseudo-recursively to process the next element.