jQuery AJAX calls in for loop

73,578

Solution 1

The easiest way is to use a closure. Whenever you have something asynchronous in a loop, it is the same thing.

for (var i .....) {
  asynchronousFunction(function() {
    use(i);
  }
}

In this pseudocode snippet, the inner function captures the storage location referenced by i. The loop runs, the i increments to its final value, and then the async callbacks start getting called, all of them looking up the exact same location (not value).

The general solution is this:

for (var i .....) {
  (function (i) {
    asynchronousFunction(function() {
      use(i);
    });
  })(i);
}

i.e. wrap the whole contents of your loop in an self-executing function.

Here, the value of outer i gets passed into the wrapping self-executing anonymous function; this unique value's location gets captured by the async callback. In this way, each async gets its own value, determined at the moment the self-executing function is invoked.

Solution 2

The links in the comment section of the question tells you what is wrong in your code... but you can have a better solution than the once explained there.

Try $.each() to iterate through the list(assuming it is an array), so that the callback passed will create a separate closure for each iteration

$.each(linkList, function (i, item) {
    $.ajax({
        url: item.getAttribute("href"),
        cache: false
    }).done(function (html) {
        var hasAppended = false;
        if (html.indexOf('someStringOnGottenPage') != -1 && !hasAppended) {
            hasAppended = true;
            var id = item.getAttribute("href").substring(item.getAttribute("href").indexOf('='));
            $("#links a[href*='" + id + "']").append(' THIS PAGE CONTAINS SPECIFIED DATA');
        }
    });
})

If it is an jQuery object then use .each()

linkList.each(function (i, item) {
    var $item = $(item),
        href = $item.attr("href");
    $.ajax({
        url: href,
        cache: false
    }).done(function (html) {
        var hasAppended = false;
        if (html.indexOf('someStringOnGottenPage') != -1 && !hasAppended) {
            hasAppended = true;
            var id = href.substring(href.indexOf('='));
            $("#links a[href*='" + id + "']").append(' THIS PAGE CONTAINS SPECIFIED DATA');
        }
    });
})
Share:
73,578

Related videos on Youtube

Edge
Author by

Edge

Updated on November 12, 2020

Comments

  • Edge
    Edge over 3 years

    I'm new to using AJAX, and I'm writing a userscript that will process a bunch of links on a page and make AJAX calls for each one.

    for (var i = 0; i < linkList.length; i++)
    {
        $.ajax({
            url: linkList[i].getAttribute("href"),
            cache: false
        }).done(function( html )
        {
            var hasAppended = false;
            if (html.indexOf('someStringOnGottenPage') != -1 && !hasAppended)
            {
                hasAppended = true;
                var id = linkList[i].getAttribute("href").substring(linkList[i].getAttribute("href").indexOf('='));
                $( "#links a[href*='" + id + "']" ).append(' THIS PAGE CONTAINS SPECIFIED DATA');
            }
        });
    }
    

    To try to put it simply, I have a page with a list of links. I wish to iterate through the links and get AJAX to process the contents of each of the links pages, and report back if that page contains something specified.

    The issue I'm having is the value of [i] used to iterate through the linkList is always 6, and it should never be. I'm assuming I need to pass some data so that when .done finally triggers, it knows its [i] value from when AJAX first triggered and not the value of [i] when .done triggers later.

    How do I go about ensuring .done knows it's [i] value when AJAX is first called?

  • Edge
    Edge over 10 years
    Adding the (function (i) { CODE })(i); wrapper works perfectly, now the code runs with the intended result. Thankyou!
  • LazerSharks
    LazerSharks almost 10 years
    So THAT's the point of a closure
  • krummens
    krummens about 7 years
    you saved my life
  • Pedro Joaquín
    Pedro Joaquín over 4 years
    It throws: "async its not defined"
  • Amadan
    Amadan over 4 years
    @PedroJoaquin: For one thing, the answer was written before async was a keyword. For another, this is a pseudocode, and async was a metavariable here, standing for any asynchronous function that accepts a callback, just like use() is a metavariable for any task that should be done inside a callback. I will rename async to make it compatible with ES6, but you still have to replace with function you are actually using (such as $.ajax in the OP).