16

I seem to be having some issues with making HEAD requests, and preserving the integrity of data in an array.

Given this snippet:

var imageTemp = Array();

$('*')
    .each(function(index){
        if($(this).css('background-image') != 'none'){
            imageTemp.push($(this).css('background-image').slice(5, -2));
        }
    });

I capture the URLs of all background-images on a given page. Now, trying to grab the size of each image via HEAD requests for Content-Length, I use this snippet:

var imageData = Array();

for(var i = 0; i < imageTemp.length; i++){
    ajaxSizeRequest = $.ajax({
        type: "HEAD",
        async: true,
        url: imageTemp[i],
        success: function(message){
            imageData.push([imageTemp[i], ajaxSizeRequest.getResponseHeader('Content-Length')]);
        }
    });
}

However, when I dump imageData via console.log, I each element (which should be an array containing the URL and the content-length) ends up as [undefined, XXXX] where XXXX is always the size of the last requested Content-Length

I'm stumped, though it appears to be a timing/scoping issue. Do I have a sort of race-condition occuring here?

Dan Lugg
  • 20,192
  • 19
  • 110
  • 174
  • 4
    Instead of an `ajaxSizeRequest` variable, you can use the `success` callback's second parameter. – SLaks Jan 17 '11 at 16:14

5 Answers5

22

The problem is that the single variables i and ajaxSizeRequest being captured by the callback function are the same variables for all instances of the callback function. I think if you call a function and pass the index variable to it and, at the same time, scope the request variable locally to the function itself use the response parameter of the done handler, you should end up with independent variables captured by the callback. It should then reference each array element and each response variable correctly.

var imageData = Array();

for(var i = 0; i < imageTemp.length; i++){
    updateImageData( i );
}

function updateImageData( i )
    $.ajax({
        type: "HEAD",
        async: true,
        url: imageTemp[i],
    }).done(function(message,text,jqXHR){
        imageData.push([imageTemp[i], jqXHR.getResponseHeader('Content-Length')]);
    });
}
Alex
  • 10,869
  • 28
  • 93
  • 165
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • Thanks alot **tvanfosson**; That works like a charm. I made `updateImageData` anonymous to avoid pollution of my script. – Dan Lugg Jan 17 '11 at 16:36
  • avoid using `success`, prevent code-clutter!, use `.done(function(text, status, xhr){ ...... };` instead. –  May 11 '15 at 18:09
  • @EladKarako - pretty old answer, I've updated to reflect current best practice. – tvanfosson May 11 '15 at 18:16
  • thanks for the update, b.t.w, answer(s) are timeless ;) (.....well.. at-least as long Google will suggest it in the first 5 results). –  May 11 '15 at 18:29
3

looks like your i isnt properly closed-in

in addition, you can't use ajaxSizeRequest because it too is pointing to just one request (probably the last, because the loop will execute very fast)

just wrap your success callback function as follows, changing the reference to ajaxSizeRequest:

success: (function(i){
   return function(data,status,xhr){
     imageData.push([imageTemp[i], xhr.getResponseHeader('Content-Length')]);
   };
})(i)
davin
  • 44,863
  • 9
  • 78
  • 78
1

You can scope I like so:

success: function(i){
    return function(message){
        imageData.push([imageTemp[i], ajaxSizeRequest.getResponseHeader('Content-Length')]);
    }
}(i)
Josiah Ruddell
  • 29,697
  • 8
  • 65
  • 67
0

If anyone still having trouble with this, and since this post is, like, 5 years-old already, here's a more 'modern' version of the answer: just use let instead of var in the original post's for loop.

Info: Is there any reason to use the “var” keyword in ES6? and: MDN - Let syntax

Community
  • 1
  • 1
Coffee
  • 95
  • 1
  • 13
0

You have a single i variable which is shared by all of the callbacks.
Since AJAX is asynchronous, all of the callbacks run after your loop is finished, and they all get the same i.

To fix this, you need to move the AJAX call into a separate function that takes i as a parameter.
Thus, each callback will get a separate i parameter.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964