12

This is to populate a table with the amount of results that are returned from the MediaWiki API query /api.php?action=query&list=querypage&qppage=BrokenRedirects. The number of results is then added to the id, for example:

// BrokenRedirects
$.getJSON('/api.php?action=query&list=querypage&qppage=BrokenRedirects&format=json', function (data) {
    $('#BrokenRedirects').text(data.query.querypage.results.length);
});

But as it's being repeated another 7 times I made the arguments for qppage into an array and used a for loop to shorten overall code.

var array = ['BrokenRedirects',
             'DoubleRedirects',
             'Unusedcategories',
             'Unusedimages',
             'Wantedcategories',
             'Wantedfiles',
             'Wantedpages',
             'Wantedtemplates'];

for (var i = 0; i < array.length; i++) {
    $.getJSON('/api.php?action=query&list=querypage&qppage=' + array[i] + '&format=json', function (data) {
        $('#' + array[i]).text(data.query.querypage.results.length);
    });
}

The first, unlooped, version works. But when I added a loop it didn't. The $getJSON part executes, but it then fails to add the resultant data to the id. I ran it through JSLint which apart from complaining about functions in a loop and declaring var i with var array returned little help. I'm relatively inexperienced with javascript so thought perhaps a variable can't be used twice within a loop? Other than that, maybe something to do with using an id within a loop?

Onei
  • 177
  • 1
  • 2
  • 11
  • possible duplicate of [Using setTimeout and an integer in a for loop](http://stackoverflow.com/questions/13731759/using-settimeout-and-an-integer-in-a-for-loop) – Denys Séguret Mar 11 '13 at 20:20
  • possible duplicate of [Javascript closure inside loops - simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Bergi Mar 11 '13 at 20:21
  • getJSON is an asynchronous call REFER: [use synchronous ajax calls][1] [1]: http://stackoverflow.com/questions/3419026/jquery-getjson-function-timing-issue – Girish Mar 11 '13 at 20:34

4 Answers4

31

That's a classical problem : i has the value of end of loop when the callback is called.

You can fix it like this :

for (var i = 0; i < array.length; i++) {
    (function(i) { // protects i in an immediately called function
      $.getJSON('/api.php?action=query&list=querypage&qppage=' + array[i] + '&format=json', function (data) {
        $('#' + array[i]).text(data.query.querypage.results.length);
      });
    })(i);
}

2018 addendum:

There's now another cleaner solution in today's browsers: use let instead of var:

for (let i = 0; i < array.length; i++) {
    $.getJSON('/api.php?action=query&list=querypage&qppage=' + array[i] + '&format=json', function (data) {
        $('#' + array[i]).text(data.query.querypage.results.length);
    });
}
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 1
    WOW! You're awesome, but JavaScript.... I truly prefer Java's `final` mechanism, much more intuitive to me – Hendy Irawan Dec 25 '13 at 21:06
  • @Denys Séguret can we set getjson inside loop equal to var promiseA and then use return data inside $.when ...then function? – user1788736 Oct 04 '15 at 10:05
3

getJSON is an asynchronous ajax call

REFER: use synchronous ajax calls

Community
  • 1
  • 1
Girish
  • 883
  • 8
  • 16
  • I did explore synchronous ajax calls, but someone pointed out that each call only executes after the previous call is completed, with each call taking somewhere between 0.5 to 1 second. Seeing as there's 8 calls made in the script, that takes me up to a potential 8 seconds for the script to load. People will wonder if it's going to load at all. – Onei Mar 12 '13 at 11:11
3

Use Jquery $.each() to iterate over the array instead of a for loop.

For example:

$.each(array, function(_, value) {
    var url = '/api.php?action=query&list=querypage&qppage=' + value + '&format=json';

    $.getJSON(url, function (data) {
        $('#' + value).text(data.query.querypage.results.length);
    });
});
Onei
  • 177
  • 1
  • 2
  • 11
0

You should write a function like -

function callUrl(value)
{
 $.getJSON('/api.php?action=query&list=querypage&qppage=' + value + '&format=json', function (data) {
        $('#' + value).text(data.query.querypage.results.length);
    });
}

and then call it with some timeout option like -

setTimeout('callUrl(+ array[i] +)',500); within the loop -

i.e.

for (var i = 0; i < array.length; i++) {
  setTimeout('callUrl(+ array[i] +)',500);
}

Some delay for each call will be required here.

Piyas De
  • 1,786
  • 15
  • 27