The Ajax examples used the XMLHttpRequest object in a synchronous manner, meaning that the moment send is called, the browser stops processing other messages and waits for an answer. To illustrate that a browser locks while processing synchronous requests, the previous Ajax application will retrieve a page from a server that will wait 10 seconds before returning the content. Following is the ASP.NET source code (note that this book will focus on both Java and ASP.NET): <%@ Page Language = "C#" %> <html> <head> <title>Hanging page</title> </head> <body> <% System.Threading.Thread.Sleep( 10000); %>
Hello, after a ten second sleep! </body>
</html>
The ASP.NET sample is written by using the C# programming language. The single statement,
System.Threading.Thread.Sleep, causes the current thread on the server to sleep for 10 seconds, which means that the browser will be waiting 10 seconds for its content to be retrieved.
Modifying the previous Ajax application and clicking the button to retrieve the hanging page causes the browser to appear similar to Figure 2-5.
In Figure 2-5, the clicked button remains pressed because it is waiting for the content to be returned. While the browser is waiting, the user cannot switch to another tab to process other HTTP requests. A hanging browser is a problem and will make the Ajax experience potentially painful for the user.
Figure 2-5. Hanging browser waiting for content to be retrieved
The solution is to use an asynchronous Ajax XMLHttpRequest request. An asynchronous request will not block the browser, and the user could continue clicking or using other tabs of the browser. The following source code rewrites the simple Ajax application to use an asyn- chronous request:
<html> <head>
<title>Sample Page</title> </head>
<script language="JavaScript" src="/lib/factory.js"></script> <script language="JavaScript" type="text/javascript">
var xmlhttp = FactoryXMLHttpRequest(); function AsyncUpdateEvent() { switch(xmlhttp.readyState) { case 0: document.getElementById('status').innerHTML = "uninitialized"; break; case 1: document.getElementById('status').innerHTML = "loading"; break; case 2: document.getElementById('status').innerHTML = "loaded"; break;
case 3: document.getElementById('status').innerHTML = "interactive"; break; case 4: document.getElementById('status').innerHTML = "complete"; document.getElementById('result').innerHTML = xmlhttp.responseText; break; } } function GetIt(url) { if(xmlhttp) {
xmlhttp.open('GET', url, true);
xmlhttp.onreadystatechange = AsyncUpdateEvent; xmlhttp.send(null); } } </script> </head> <body>
<button onclick="GetIt('/chap02/serverhang.aspx')">Get a document</button> <p><table border="1">
<tr>
<td>Document</td> <td>
<span id="status">No Result</span> </td>
<td>
<span id="result">No Result</span> </td></tr>
</table></p> </body> </html>
There are several new additions to the rewritten Ajax application, and they deal with the technical issues of loading content asynchronously. Let’s start by focusing on the function
GetIt. The implementation of GetIt is similar to previous Ajax application examples, except that the third parameter of the method open is true to indicate that the request will be asyn- chronous. This means that when the method send is called, the method will send the request, start another thread to wait for the response, and return immediately.
Whenever XMLHttpRequest operates in asynchronous modes, feedback is given to the caller on the state of the request. The property onreadystatechange is a function that receives the feedback. It is important to note that the feedback function must be assigned before each send
because upon completion of the request, the property onreadystatechange is reset. This is evident in the sources of the Mozilla-based browsers.
The property onreadystatechange is assigned the function AsyncUpdateEvent. In the imple- mentation of AsyncUpdateEvent is a switch statement that tests the current state of the request. When an asynchronous request is made, the script is free to continue executing other code.
This could cause problems if the script attempts to read the request results before the request has been completed. Using the property readyState, it is possible to know the stage of the HTTP request. The property readyState can contain one of five values, each representing a request state:
• 0: The XMLHttpRequest instance is in an inconsistent state, and the result data should not be referencing.
• 1: A request is in progress, and the result data should not be retrieved.
• 2: The request has downloaded the result data and is preparing it for reference. • 3: The script can interact with the XMLHttpRequest instance even though the data is not
completely loaded.
• 4: The request and result data are completely downloaded and loaded as an object model. The request states seem to indicate that it is possible to manipulate various properties at different states. The problem is that not all browsers support the same property states at the same state codes. The only cross-platform solution is to reference the XMLHttpRequest result properties (status, statusText, responseText, and responseXML) when the request state is equal to 4. When the request state is 4, you can be sure that the result properties contain a valid value. Executing the asynchronous Ajax application results in a call being made, and the browser is not locked. You can click the button, open a new browser, and surf to another website. After the 10 seconds have expired, the generated HTML page should resemble Figure 2-6.
Figure 2-6. Resulting HTML page using asynchronous XMLHttpRequest
The asynchronous approach solves the problem of the hanging browser. The Ajax applica- tion could continue processing other data, and in fact multiple requests could be made.
What is not optimal is that there is no busy indicator. You have no idea whether anything is working when you click the OK button. There should be some form of indicator that some- thing is happening.
Another problem is that some browsers will cache the results of the XMLHttpRequest. This is an age-old problem because caching can result in unpredictable behavior, and caching still happens even if the Ajax HTML page is reloaded.