-1

In a chrome extension I am writing I need to validate some objects I have stored in localStorage against my server . For this I created a for loop that simply sends the data for each localstorage element and that needs to do something with the response . The code is something like this:

 for (var key in localStorage) {
        if (! some condition )
                      continue;

        var httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = function() {
            if (httpRequest.readyState === 4 && httpRequest.response == 200) {
                do something with 'key' from the for loop
            }
        }
        };
        var url = BASE_PATH ;
        httpRequest.open("POST", url);
        httpRequest.send(some data);
                     }

However while debugging it seems that in the ajax response , the 'key' from the for loop isn't the key I need : I was expecting to get the same key from the for loop that matches each ajax call , what I got was the same key for all ajax calls . Am I doing something wrong or expecting something that isn't possible ? I thought that since the ajax is inside a closure function , the values are kept in memory or something of the sort .

Joel Blum
  • 7,750
  • 10
  • 41
  • 60
  • possible duplicate of [Javascript closure inside loops - simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Felix Kling May 14 '13 at 14:49
  • There is a ")" that shouldn't be there if (httpRequest.readyState === 4) && httpRequest.response == 200) – Niccolò Campolungo May 14 '13 at 14:52

1 Answers1

1

Extract the actual Ajax call to a separate function:

function AjaxCall( key ) {
  var httpRequest = new XMLHttpRequest();
  httpRequest.onreadystatechange = function() {
    if ((httpRequest.readyState === 4) && (httpRequest.response == 200)) {
      do something with 'key' from the for loop
    }
  };
  var url = BASE_PATH ;
  httpRequest.open("POST", url);
  httpRequest.send(some data);
}

for (var key in localStorage) {
  if ( some condition ) {    
    AjaxCall( key );
  }    
}

Reason is, that you are creating a closure with the function passed to onreadystatechange. All those functions point to the same key variable, which holds just one (the last) value for all Ajax calls, after the loop is finished.

When you create a separate function (like the AjaxCall() in my example), you create a different context for each call and hence all callbacks point to different keys.

Sirko
  • 72,589
  • 19
  • 149
  • 183
  • As I said in the question, there is a ) that shouldn't be there. Besides, the "else" is not necessary, because if the continue is reached the code below won't be executed. However, good explanation. – Niccolò Campolungo May 14 '13 at 14:57
  • Also, in your for loop, if you are not already using the hasOwnProperty method you may want to try it. This will prevent you from sending a property name or function name that is inherited from the Object type. – jhorton May 14 '13 at 14:57
  • @jhorton: None of the native properties defined at `Object.prototype` are enumerable. It will be fine as long as no custom, enumerable properties are defined on the prototype. – Felix Kling May 14 '13 at 15:00