Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the Ajax function I call in my script run twice in a row when readyState is 4?

All,

I am studying Ajax using the Head First Ajax book. In the first chapter they give some code example which I simplified a bit. I added a bunch of alert to understand what was happening. Here is the code:

HTML + Ajax (index.php):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Rob's Rock 'n' Roll Memorabilia</title>
<link rel="stylesheet" href="css/default.css" />
<script>
  function createRequest() {
    try {
      request = new XMLHttpRequest();
    } catch (tryMS) {
      try {
        request = new ActiveXObject("Msxml2.XMLHTTP");
      } catch (otherMS) {
        try {
          request = new ActiveXObject("Microsoft.XMLHTTP");
        } catch (failed) {
          request = null;
        }
      }
    }
  return request;
}

function getDetails(img){
  var title = img.title;
  alert("getDetails1");
  request = createRequest();
  alert("getDetails2");
  if (request == null) {
    alert("Unable to create request");
    return;
  }
  var url= "getDetails.php?ImageID=" + escape(title);
  alert("getDetails3");
  request.open("GET", url, true);
  alert("getDetails4");
  request.onreadystatechange = displayDetails;
  alert("getDetails5");
  request.send(null);
  alert("getDetails6");
}

function displayDetails() {
  alert("displayDetails1");
  if (request.readyState == 4) {
    alert("Request.readyState is 4");
    if (request.status == 200) {
      alert("Request.status is 200");
      detailDiv = document.getElementById("description");
      alert("displayDetails2");
      detailDiv.innerHTML = request.responseText;
      alert("displayDetails3");
    }else{
      alert("Request.status not 200");
      return;
    }
  }else{
    alert("Request.readystate not 4");
    return;
  }
}
</script>  
</head>
<body>
  <div id="wrapper">
    <div id="thumbnailPane">  
      <img src="images/SISL_Avatar2.JPG"
           title="SISL" id="SISL" onclick="getNextImage()" />  
      <img src="images/itemGuitar.jpg" width="301" height="105" alt="guitar" 
           title="itemGuitar" id="itemGuitar" onclick="getDetails(this)"/>
      <img src="images/itemShades.jpg" alt="sunglasses" width="301" height="88" 
           title="itemShades" id="itemShades" onclick="getDetails(this)" />
      <img src="images/itemCowbell.jpg" alt="cowbell" width="301" height="126" 
           title="itemCowbell" id="itemCowbell" onclick="getDetails(this)" />
      <img src="images/itemHat.jpg" alt="hat" width="300" height="152" 
           title="itemHat" id="itemHat" onclick="getDetails(this)" />
    </div>

    <div id="detailsPane">
      <img src="images/blank-detail.jpg" width="346" height="153" id="itemDetail" />
      <div id="description"></div>
    </div>

  </div>
</body>
</html>

<?php    
$details = array (
    'itemGuitar'    =>  "<p>Pete Townshend once played this guitar while his own axe was in the shop having bits of drumkit removed from it.</p>",
    'itemShades'    =>  "<p>Yoko Ono's sunglasses. While perhaps not valued much by Beatles fans, this pair is rumored to have been licked by John Lennon.</p>",
    'itemCowbell'   =>  "<p>Remember the famous \"more cowbell\" skit from Saturday Night Live? Well, this is the actual cowbell.</p>",
    'itemHat'       =>  "<p>Michael Jackson's hat, as worn in the \"Billie Jean\" video. Not really rock memorabilia, but it smells better than Slash's tophat.</p>"
);    
if (isset($_REQUEST['ImageID'])){echo $details[$_REQUEST['ImageID']];}
?>

Here is the URL that is called by Ajax (getDetails.php):

<?php

$details = array (
    'itemGuitar'    =>  "<p>Pete Townshend once played this guitar while his own axe was in the shop having bits of drumkit removed from it.</p>",
    'itemShades'    =>  "<p>Yoko Ono's sunglasses. While perhaps not valued much by Beatles fans, this pair is rumored to have been licked by John Lennon.</p>",
    'itemCowbell'   =>  "<p>Remember the famous \"more cowbell\" skit from Saturday Night Live? Well, this is the actual cowbell.</p>",
    'itemHat'       =>  "<p>Michael Jackson's hat, as worn in the \"Billie Jean\" video. Not really rock memorabilia, but it smells better than Slash's tophat.</p>"
);
echo $details[$_REQUEST['ImageID']];
?>

The question: Why does the function displayDetails run twice at readystate 4?

When I run the above, the code seems to run through the displayDetails() function twice. I first get the displayDetails1 alert signaling I've entered the function. I then get the alerts related to the readyState not being 4, then not being 4 again, then being 4 (Request.readyState is 4). Then the status alert tells me the status is 200. Up to now, nothing unexpected.

After that I get something weird. I get the displayDetails2 alert, then the page is modified according to the function and I get the displayDetails3 alert. I then expect to exit the function. Instead I get again the displayDetails1, Request.readyState is 4 (a second time!), Request.status is 200, displayDetails2, and displayDetails3 alerts, as if the entire function had run a second time. Why is that?

PS:
1) After the 2nd round, I then get the getDetails6 alert I expect.
2) The page functions as it should - from a user's standpoint there's nothing unusual if the alerts are disabled. 3) I am developing locally on WampServer, on a WinXP machine (I know...).
4) If I add:

function alert(msg) {
  console.log(msg);
}

to my script the log only registers one readyState is 4...

RESOLUTION

I have done 3 tests:
1 - with the alerts only, I get two readyState is 4 alerts.
2 - if I log the alerts, the log only shows one readyState is 4 alert.
3 - if I both log and display the alert pop-ups (using this function), I get two readyState is 4 alerts (and the log shows that).

My take on this is that it is the alerts themselves that cause a script execution delay and cause the function to effectively run twice.

like image 243
JDelage Avatar asked Feb 05 '13 19:02

JDelage


1 Answers

The javascript alert is blocking your UI thread, probably long enough for your browser to finish loading the AJAX request. Since you don't check the request.readyState until after the alert, it can be updated by the browser before you check it.

Try modifying your event handler:

function displayDetails() {
  var rs = request.readyState;
  alert("displayDetails1");
  if (rs == 4) {
    alert("Request.readyState is 4");
    //rest of code...

You will see just one alert of "Request.readyState is 4"

like image 107
Jeff-Meadows Avatar answered Nov 10 '22 00:11

Jeff-Meadows