Question

I am struggling with this for a day now, and going in circles and help would be greatly appreciated :-)

Abstract
Asynchronous ajax calling cgi with resolver and fqdn variables in order to return a dns resolution agains this pair. (returns the output of dig @resolver $fqdn)

Problem
In firebug I can see that get requests are being fired asynchronously and responses to the browser are as expected. However I cannot place the responses in correct divs in doc as onreadystatechange doesn't recognize the objects.

Side note
Apart from the fact that I am iterating through the array of objects It appears that they all are fired instantaneously even when delay between iterations is placed.

Below is a code with my comments
As resolver is an array I created an array of xmlhttprequest objects.

function resolve() { 
     var numofres = 6;
     var arr = new Array;
     arr[0] = "192.168.1.11";
     arr[1] = "8.8.8.8";
     arr[2] = "8.8.4.4";
     arr[3] = "159.134.0.1";
     arr[4] = "159.134.0.2";
     var len = arr.length;
     var ax  = new Array(); //creating ax as an array 
     for (var i=0; i<=len; i++) {  //iterating through the length of resolvers array
         ax[i] = new XMLHttpRequest(); //assigning to each item in array new object
         //alert(ax[i]); // shows that object exists 
         ax[i].onreadystatechange = function(){ 
              /*=== 
              problem is above - firebug will show:
              **Uncaught TypeError: Cannot read property 'readyState' of undefined**
              **ax.(anonymous function).onreadystatechangehello.cgi:30** 
              oddly it will still populate divs inner html  with 'loading +1 '
              albeit regardless of readystate code (can be 4 or anything else )
              It perplexes me why i is thought as a function? 
              =====*/
              // alert(i);  //if this is enabled I will see readyState==4 populated correctly
              if (ax[i].readyState != 4) {
                   document.getElementById('return_table_'+i).innerHTML="loading "+i;
              }
              if(ax[i].readyState == 4){
                  // get data from the server response
                  var response_ready=ax[i].responseText;
                  document.getElementById('return_table_'+i).innerHTML = response_ready;
              }
         } 
         ax[i].open("GET","av.pl?resolver=" + arr[i] +"&fqdn=google.com",true); //works 
         ax[i].send(null); //works 
      }

 }
Était-ce utile?

La solution

Your problem is an extremely common one. In JavaScript, variables are scoped at the function level, not at the block statement level. Thus, as you iterate through that loop with the variable "i", each function you create in the loop shares the same "i". Thus when the functions are actually called, the value of "i" will be what it is at the end of the loop — and that's a point beyond the end of the array!

To avoid the problem, you need to create those functions in another function. A clean way to do that is to have a separate local function:

 function makeReadyStateHandler(i) {
     return function() {
          if (ax[i].readyState != 4) {
               document.getElementById('return_table_'+i).innerHTML="loading "+i;
          }
          if(ax[i].readyState == 4){
              // get data from the server response
              var response_ready=ax[i].responseText;
              document.getElementById('return_table_'+i).innerHTML = response_ready;
          }
     };
 }

Then just call that function from the loop:

      ax[i].onreadystatechange = makeReadyStateHandler(i);

By using a separate function like that, you assure that each handler function will have its own copy of "i" that's frozen at the correct point in the loop. The function will return a newly-created function as its result, which you'll use as the event handler.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top