admin管理员组

文章数量:1330159

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"
        ".dtd">
<html xmlns="">
<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.

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/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3/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.

Share Improve this question edited May 23, 2017 at 11:44 CommunityBot 11 silver badge asked Feb 5, 2013 at 19:01 JDelageJDelage 13.7k23 gold badges82 silver badges114 bronze badges 7
  • 1 I didn't study this too much because I think your issue is a non-issue. What you are doing when this line runs request.onreadystatechange = displayDetails is initiating an on-event method. The "readystate" changes (I think) 4 times when initiating an async call. And each time it changes it calls that function. It is only when the readystate returns a 4 that the "if" statement runs. So what you need to look at more is the readystate process and the onEvent method relationship – user416527 Commented Feb 5, 2013 at 19:12
  • 1 Try using the console and breakpoints rather than alerts. You will get much more information to help debug. – Corey Commented Feb 5, 2013 at 19:13
  • Here are the different readyState stages 0 = uninitialized; 1 = loading; 2 = loaded; 3 = interactive; 4 = plete – user416527 Commented Feb 5, 2013 at 19:15
  • @Jimmy - Why does the function run twice once readyState is 4? – JDelage Commented Feb 5, 2013 at 19:22
  • 1 In addition to the behavior you explicitly ask about, it's also strange that you don't get your getDetails6 until after the request has pleted; that implies that your request is synchronous, but request.open("GET", url, true) should create an asynchronous one. – ruakh Commented Feb 5, 2013 at 19:49
 |  Show 2 more ments

2 Answers 2

Reset to default 7

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"

The onreadystatechange event isn't only triggered after the request has pleted. There are 5 states defined for the XMLHttpRequest:

  • 0 Uninitialized The initial value.
  • 1 Open The open() method has been successfully called.
  • 2 Sent The UA successfully pleted the request, but no data has yet been received.
  • 3 Receiving Immediately before receiving the message body (if any). All HTTP headers have been received.
  • 4 Loaded

Normally when developing AJAX applications you will be only interested the state 4 - loaded, as it the means the request has finished, but there are other states that you can observe.

So you don't have to worry about that second call to you event handler. This is normal behaviour. It just means the request has changed its inner state during its work.

If you are interested in that states I would advice you to log all states to the javascript console to see what is happening. I would also give you the tipp test this when loading large data. It is likely that you'll see multiple 3 - recieving - states.


Update, maybe I understand your question wrong (but not your code ;) .. You told in ments that you see two times ready state 4. I've tested this at home and the 100ths of alerts looked weird to me.

I then added the following function to your javascript section:

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

This redirects all alerts to the javascript debug console. I got the following output:

I see only one time ready state 4. Seems that you selfish gone wired because of so much alert() ;)


Update2: As I mentioned in the ments and @thanks Jeff-Meadows who also pointed this out: Note that the alert() function will block the javascript thread unless you press the ok button. As asynchronous XHR requests will being handled by a separate browser thread, the XHR will continue loading while javascript blocks on the alert message. You see that this can / will unwanted side effects and makes alert() less worth for debugging purposes.

本文标签: phpWhy does the Ajax function I call in my script run twice in a row when readyState is 4Stack Overflow