MSc-IT Study Material
June 2010 Edition

Computer Science Department, University of Cape Town

Performing asynchronous communication

When creating an interactive application it is important to not let the application unduly freeze while it is performing an action. This is equally true when making a request to a server using an XMLHttpRequest object: when making a request from the server, the interface should remain usable, responding to actions from the user, even if only to say that the Web application is currently busy. One important place that special care may be required is when the XMLHttpRequest results in enough information being transferred from the server that there is a noticeable delay in the browser receiving it. For instance, this can easily occur when downloading a large document to embed in the Web page. But remember that often there can be delays even when transferring very small amounts of information, since some users may be connecting to the Web application using slower connection methods, or data may have been lost during transit.

Application freezing usually occurs when send is used and the open method – which you should remember is called just before calling send – was called with the third argument set to false. In this case send does not return until it has finished receiving all the data being sent from the server. If it takes ten seconds to receive the data, it takes ten seconds for the send function to return from its call. And for those ten second the browser will not perform any other action, including accepting user input.

We call the form of communication where we wait for the server to finish sending the browser information before continuing to execute the Web application synchronous communication. As previously mentioned, AJAX allows for asynchronous communication: while the data is being transferred, the Web application can continue to execute, and can accept user input or perform other functions.

Asynchronous communication is fairly easy to perform: instead of passing false to the open function, we pass true to indicate that we want to communicate asychronously with the server. When send is then called, send will immediately return. In the synchronous case this would have meant that communication with the server is now complete, and any information that it was sending to the browser is now available. In the asynchronous case this is not true. Rather, we supply the XMLHttpRequest object with a function which it should call when the state of the communication changes. For instance, the method is called when the communication begins, and when the communication ends. The XMLHttpRequest object has a property called readyState, which indicates when the communication has finished: if it holds the integer value 4, then communication is done, and the data is ready to be used. The data may then be read from the XMLHttpRequest object in the usual way.

The example for this section is a rewrite of the Random Web application we developed above so that it performs its request for a random number asynchronously. Begin by copying the contents of the random directory to a new directory in webapps called async-random.

Edit the index.html file so that it contains the following:

<html> 
	<head> 
		<title>Press a button to get a number!</title> 

	</head> 
	<body> 
	 
	<script type="text/javascript"> 

	function create_request() { 
		var httpRequest; 
		if (window.XMLHttpRequest) { 
			httpRequest = new XMLHttpRequest(); 
		} 
		else if (window.ActiveXObject) { 
			httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); 
		} 
		else 
		{ 
			document.write("Unable to create XMLHttpRequest object."); 
		} 
		return httpRequest; 
      	} 

	function get_data_asynchronously_from(file_url, variables, event_handler) 
	{ 
	 
		var httpRequest = create_request(); 

		httpRequest.onreadystatechange = function() {	 
			event_handler(httpRequest); 
		} 
		 
		if (variables != null) 
		{ 
			httpRequest.open("POST", file_url, true); 
			httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 
			httpRequest.send(variables); 
		} 
		else 
		{ 
			httpRequest.open("GET", file_url, true); 
			httpRequest.send(null); 
		} 
		 
	} 
	 
	function is_data_ready(request) 
	{ 
		return request.readyState == 4; 
	} 
	 
	function get_value(request) 
	{ 
		if (request.status != 200) // For non http requests. 
		{ 
			alert("There has been an error reading the URL. " + request.status); 
			return null; 
		} 
		else 
		{ 
			return request.responseText 
		} 
	} 

	function button_press() 
	{ 
		function set_value(request) 
		{ 
			if (is_data_ready(request)) 
				document.getElementById("display-div").innerHTML=get_value(request); 
		} 
				 
		get_data_asynchronously_from("http://localhost:8080/random/random", null, set_value); 
	} 

	</script> 

	<h1> Random numbers from the server: </h1> 
	<form> 
		<input type="button" value="Print a number!" onclick="button_press();"/> 
    	</form> 
	<div id="display-div"> 
		<p>The button hasn't yet been pressed.</p> 
	</div> 
  </body> 
</html> 
    

The first large change is the get_data_asynchronously_from() function, which now accepts a third argument, which will be the name of a function to be used in the XMLHttpRequest onreadystatechange event handler: this is the function that will handle the data retrieved from the server. The function passed to get_data_asynchronously_from() should always accept one argument, which will be the XMLHttpRequest object which is retrieving the data. This allows the function to test if the server has finished sending the data, and to obtain the data from the object. The first two arguments to get_data_asynchronously_from() are exactly the same as previously.

After creating the XMLHttpRequest object, the first thing that get_data_asynchronously_from() does is to set the event handler which will be called when the data is ready. Note that the event handler for onreadystatechange does not accept any arguments, so a new function is created which calls the event_handler function passed to get_data_asynchronously_from() and passes it the XMLHttpRequest object.

The next if statement determines if any data (contained in the variables variable) is being sent to the server, and decides on whether POST or GET request methods should be used.

Finally, send is called, either passing the data or passing null, as necessary.

There are two new functions: is_data_read() takes a XMLHttpRequest object and returns true if communications with the server has completed; otherwise it returns false.

get_value() takes an XMLHttpRequest object and is exactly the code from the old get_data_synchronously_from() functions that tests to see what status the server has given for the HTTP request, and returns the data.

button_press() has also been updated. First, it creates a function called set_value, which is the event handler we will pass to get_data_asynchronously_from. This function first tests to see that the XMLHttpRequest object has completed communications with the server (since the event handler is called whenever communication state with the server changes, and not only when communication completes). It then retrieves the sent data, and updates the Web page as in the previous example.