views:

56

answers:

3

I need to get a range of pages using AJAX and put them into an array, where their given place in the array is equal to the i of a for loop (it's a caching-like function for blog pages, and the range of the for loop is entirely variable). I'm doing something akin to the following:

var bongo = new Array();

for (i = 0; i < 10; i++) {

    jQuery.ajax({ type: "GET", url: http://localhost, data: queryString, success: function(request) { bongo[i] = request } })

}

The problem is, that unless I add async: false to the .ajax options (which would make it... SJAX?), which causes the requests to basically pause the browser, going against what I'm trying to do, the i in the success callback will always end up being 11, whereas I of course want it to pour the returned data into each slot of the array from 0 to 10.

I've tried replacing the line with this:

bongo[i] = jQuery.ajax({ type: "GET", url: http://localhost, data: queryString }).responseText

But that made no difference.

A: 

I think the problem is that you did not declare i as a local variable like so:

for (var i=0; i<10; ++i)

Jeremy
Sorry, that was the example code; I've done that in the real code.
Heilemann
+3  A: 

Try:

var bongo = [];
for (i=0; i<10; i++) {
  $.get("http://localhost", function(result) {
    bongo.push(result);
  }
}

This way each result will simply get pushed onto the array, solving the need for the array indexes to be correct. Order however is not guaranteed. If that is a requirement you'll need another approach.

There are multiple ways to solve this problem. Here is one: create objects for your callback to save state. Here is an example:

function Callback(array, index, result) {
  this.array = array;
  this.index = index;
  this.result = result;
  var obj = this;
  this.func = function() {
    obj.array[obj.index] = obj.result;
  };
}

$(function() {
  var arr = [];
  for (var i=0; i<4; i++) {
    var obj = new Callback(arr, i, "result" + i);
    setTimeout(obj.func, (5-i) * 100);
  }
  setTimeout(function() {
    console.log(arr);
  }, 500);
});

So in your case:

function Callback(array, index) {
  this.array = array;
  this.index = index;
  var obj = this;
  this.callback = function(result) {
    obj.array[obj.index] = result;
  };
}

var bongo = [];
for (i=0; i<10; i++) {
  var ob = new Callback(bongo, i);
  $.get("http://localhost", ob.callback);
}

Basically the above saves all the data to an object and thus each callback has access to the right information.

Also, bear in mind that most browsers limit the number of concurrent AJAX requests, typically to 2 per host.

cletus
But this way I can't decide exactly which place in the array the data is pushed to, which is what I need.
Heilemann
@Heilemann added a version that saves the indexes in the correct order.
cletus
+2  A: 

You need a closure:

var bongo = [];
for (i = 0; i < 10; i++)
{

  (function(i)
    {
      jQuery.ajax(
        {
          type: "GET",
          url: "http://localhost",
          data: queryString,
          success: function(request) { bongo[i] = request } 
        });  
    })(i);
}

Loops are the #1 place where inline functions stump people. The bongo[i] = result isn't called until later. The value of i at that time is different (most likely 10). If you want to "trap" or "capture" the current value of i, you need to create a new scope. The only way to do that in javascript is with another function.

Plynx