Implementing the Asynchronous Class in Ajax

an article added by: Sonja Lande at 06012007


In: Categories » Computers and technology » AJAX » Implementing the Asynchronous Class in Ajax

Having determined asynchronous XMLHttpRequests are the way to go, the Asynchronous encapsulation class is defined. An encapsulation class is not necessary, but it makes it simpler to work with the XMLHttpRequest object. The main advantage of using an encapsulation class is the association of the XMLHttpRequest object with a code block. For a refresher, let’s look at the code to load an Atom feed again:

   function LoadAtomFeed() {
   var asynchronous = new Asynchronous();
   asynchronous.settings = {
   onComplete : function(xmlhttp) {
   parseAtom( xmlhttp.responseXML);
   flexbox.update();
   }
   }
   asynchronous.get("/services/blog/entries/current");
 }

To make the Asynchronous class work, the user needs to do two things: associate the settings data member with some information and call an appropriate method (e.g., get) to make an HTTP request. The purpose of the settings data member is to provide the callbacks and extra information associated with a request. In the example, the onComplete method is called once the HTTP request has completed.

The code as it stands is fairly easy to understand, but remember that the code is asynchronous. As a result, when the asynchronous.get method is called, the LoadAtomFeed function will exit before calling onComplete and returning control to the user. In a worst-case scenario, if a user goes click-crazy, there could be dozens of requests being made. The alternative is to use synchronous calls, which lock the browser, and that itself is not an option. On the positive side, asynchronous behavior allows a user to launch tasks and wait for the results. The programmer has to remain vigilant and make sure that the user does not get ahead of him- or herself. Now that you have a basic understanding of how to use the Asynchronous class, let’s look at the details of the Asynchronous implementation. The class exposes a method for each HTTP verb, and in the example the get method corresponds to the HTTP verb GET. The implementation of get is as follows:

   function HttpRequest_get(strurl) {
   this.call({ action : "GET", url:  strurl});
 }

In the implementation of HttpRequest_get, which is mapped to Asynchronous.get, a reference to the function call is made. The get function is a convenience method to the call method. The call method accepts a single parameter: an object with specific data members. In the case of the get method, the action and url data members are defined. So why create an object, when you could have used two parameters, action and url, for the method call? The answer is flexibility. JavaScript does not understand overloaded functions, and there can be only a single function with a single set of parameters. So that the developer does not have to play the permutations and combinations game with the parameters (e.g., which parameters, what order, etc.), an object with data members is created.

The most important method of Asynchronous is call, because regardless of which HTTP verb is used, the method is executed. The following code is the complete implementation of call.

   Source: /client/scripts/jaxson/communications.js
   function Asynchronous_call(request) {
   var instance = this;
   if (! this.settings) {
   throw new Error("Settings is not defined  ");
   }
   if (this.xmlhttp.readyState != 4 &&  this.xmlhttp.readyState != 0) {
   throw new Error("Currently active request  cannot continue");
   }
   this.xmlhttp.open(request.action, request.url,  true,
   this.settings.username,  this.settings.password);
   if (request.headers) {
   for( defHeader in request.headers) {
   this.xmlhttp.setRequestHeader(defHeader,  request.headers[defHeader]);
   }
   }
   if (this.settings.headers) {
   for( defHeader in this.settings.headers) {
   this.xmlhttp.setRequestHeader(defHeader,
   this.settings.headers[defHeader]);
   }
   }
   this.xmlhttp.onreadystatechange = function() {
   switch (instance.xmlhttp.readyState) {
   case 1:
   if (instance.settings.onLoading) {
   instance.settings.onLoading(instance.xmlhttp);
   }
   break;
   case 2:
   if (instance.settings.onLoaded) {
   instance.settings.onLoaded(instance.xmlhttp);
   }
   break;
   case 3:
   if (instance.settings.onInteractive) {
   instance.settings.onInteractive(instance.xmlhttp);
   }
   break;
   case 4:
   if (instance.settings.onComplete) {
   try {
   instance.settings.onComplete(instance.xmlhttp);
   }
   catch (e) {
   globals.errorHandler(e);
   }
   }
   break;
   }
   }
   try {
   this.xmlhttp.send(request.data);
   }
   catch( e) {
   globals.errorHandler(e);
   }
 }

I’ll now explain the call method from beginning to end, and I’ll reiterate the individual code pieces, with some reiterations abbreviated. I show the code again where appropriate for ease of understanding, as the implementation of call is relatively long and does many different things. Starting at the top, the instance variable is assigned to the this value: var instance = this; This assignment is necessary because a code block will be assigned to the onreadystatechange data member, and the code block has to remember which Asynchronous instance is being referenced. After the assignment, we validate that the settings data member has been populated:

   if (! this.settings) {
   throw new Error("Settings is not defined  ");
 }

The settings data member has to exist because it contains information about which methods to call when the request has completed and which HTTP headers to add to the request. At this point, you should notice that two objects are required to make the call method work. The first object is settings, and the second object is the request parameter. As a shortcut, a developer could combine the two objects into one, and either pass it to the call method or assign it to the settings data member. This shortcut would be misguided, because each object has a different purpose.

The request object is used to define the settings for the specific URL request being made. The settings object is used to define settings that transcend all calls and could be a considered a global callback for the Asynchronous object instance. The idea is that you allocate an instance of Asynchronous, define how it should behave, and then start making requests with the possibility of calling different URLs. Then the next step is to verify that the current XMLHttpRequest instance is not already preoccupied:

   if (this.xmlhttp.readyState != 4 &&  this.xmlhttp.readyState != 0) {
   throw new Error("Currently active request  cannot continue");
 }
 

When using XMLHttpRequest in asynchronous mode, it is possible to make a new HTTP request on an executing XMLHttpRequest instance. The problem is that XMLHttpRequest cannot serve two requests, and it will stop the already executing request to make a new request. The readyState property is used to indicate the state of the XMLHttpRequest object. If readyState is 0, then no request has been made and an HTTP request can be made. If readyState is 4, then a request has been made, the request has completed, and the object is ready to make another request. If readyState has any other value, a request is being executed, and thus executing another request does not make sense. After the initialization and checking has completed, it is possible to make a request: this.xmlhttp.open(request.action, request.url, true, this.settings.username, this.settings.password); Calling the open method, you need to specify at aminimum the HTTP verb (e.g., GET, POST) via the request.action data member and the URL via the request.url data member. Whenever you make any XMLHttpRequest request, make sure that the first action is the open method. If you assign the onreadystatechange function before calling open, your request will not work properly. If you attempt to assign request headers before calling open, your request will not work properly. The open method is used to initialize and create a new request; open will not call the server, as that is the responsibility of send.

In the open method example, a username and password (this.settings.username and this.settings.password, respectively) are always sent to the server, even if there is no username and password. It does no harm to send null values, and it will not affect the operation of an HTTP request. The username and password are used only when the HTTP server performs a challenge and asks for a username and password. In the context of the XMLHttpRequest object, the username and password are used only if the underlying implementation of XMLHttpRequest needs the information. After you’ve opened the request, you can add custom HTTP headers using the setRequestHeaders method:

   if (request.headers) {
   for( defHeader in request.headers) {
   this.xmlhttp.setRequestHeader(defHeader,  request.headers[defHeader]);
   }
   }
   if (this.settings.headers) {
   for( defHeader in this.settings.headers) {
   this.xmlhttp.setRequestHeader(defHeader,
   this.settings.headers[defHeader]);
   }
 }

The headers are assigned two times: once for the settings associated with the request (request.headers) and once for the global settings (this.settings.headers). The headers that you assign can be standard HTTP headers or custom headers. If you plan on using custom HTTP headers, it is always a good idea to prefix them with X-. That way, your headers will not be confused with standard HTTP headers. After you’ve assigned the headers, the onreadystatechange callback function is assigned:

   this.xmlhttp.onreadystatechange = function() {
   switch (instance.xmlhttp.readyState) {
   case 1:
   if (instance.settings.onLoading) {
   instance.settings.onLoading(instance.xmlhttp);
   }
   break;
   case 2:
   if (instance.settings.onLoaded) {
   instance.settings.onLoaded(instance.xmlhttp);
   }
   break;
   case 3:
   if (instance.settings.onInteractive) {
   instance.settings.onInteractive(instance.xmlhttp);
   }
   break;
   case 4:
   if (instance.settings.onComplete) {
   try {
   instance.settings.onComplete(instance.xmlhttp);
   }
   catch (e) {
   globals.errorHandler(e);
   }
   }
   break;
 }

In the Asynchronous implementation of onreadystatechange, a switch statement executes one of the four valid ready states. From the source, you can associate each number with a specific state. The most important ready state is 4, indicating that the content has been retrieved, loaded, and processed. The 4 ready state calls the method defined by the client in the settings property. Notice in the implementation of the assigned onreadystatechange function that the instance variable has replaced a reference to this.

The implementation of the onreadystatechange function is overkill, as 98% of the time you use Asynchronous you will only care about the ready state of 4. For all of the other ready states, depending on the browser, certain functionality is possible. Because of this inconsistency and the incompleteness of the XMLHttpRequest object state, there is no real need to process the other ready states. The remaining call function code involves sending the request to the server using the send function:

   try {
   this.xmlhttp.send(request.data);
   }
   catch( e) {
   globals.errorHandler(e);
 }

The send function is used to make the physical request to the server. The function is also used to send any data associated with the request. If there is no data, then calling send with null value will not generate an error. When the send method has made its request, it returns immediately, and the JavaScript Asynchronous_call function will return control to the browser. At this time, the client should add some code to not allow multiple calls. For example, if the request was activated using button, then until the request has returned, the button could be disabled. Of course, this introduces a potential problem if the request never returns and the button remains disabled. To solve that problem, you can introduce a timer that gives the request a maximum amount of time. If during that time, the request still has not returned, the XMLHttpRequest.abort method is called and the button is enabled again.

legal notice

Our website is not responsible for the information contained by this article. Web-articles is a free articles resource.
Suggestion: If you need fresh, daily updated content for your website, feel free to use our service. Click here for more information.

Useful tools and features

Link to this article from your page    Send this article to you or to a friend
If you like this article (tutorial), please link to it from your web page using the information above.

related articles

1. Ajax
Given the recent interest in Ajax, you’d be forgiven for thinking it was a new technology. In fact, the XMLHttpRequest object has been around for years. In technical terms, asynchronous JavaScript interaction with the server is nothing new. All of the other elements of the Ajax model have also been around for quite some time: CSS, (X)HTML, and DOM Scripting. Yet in 2005, interest in this methodology soared. Could it really be that simply giving this approach a snappy name like Ajax was responsible for the sudde...

2. Understanding the Definition and Philosophy of Ajax
The focus of this article is to provide solutions to some common, general problems and questions that are bound to arise before or during development of Asynchronous JavaScript and XML (Ajax) and Representational State Transfer (REST) applications. These common questions are not always technical in nature, often leaning more toward theory or philosophy of development. The problem with these kinds of questions is that once you begin to think about them, you keep going in a circle and end up where you star...

3. Understanding the Definition and Philosophy of Web Services and SOA
Understanding the Definition and Philosophy of Web Services and SOA Wikipedia offers the following definition of Web services:4 The W3C defines aWeb service as a software system designed to support interoperable machine-to-machine interaction over a network. This definition encompasses many different systems, but in common usage the term refers to those services that use SOAPformatted XML envelopes and have their interfaces described by WSDL. For ex...

4. Understanding the Definition and Philosophy of REST
Understanding the Definition and Philosophy of REST REST is a controversial topic among Web service enthusiasts, because it’s considered to stand for the opposite of what Web services and SOA are trying to achieve. The problem with this thinking is that REST is not in contradiction with the abstract definition of SOA and Web services. REST is in contradiction with technologies such as SOAP, WSDL, and WS-* specifications. The following offers a quick definition of REST:...

5. The Easiest Way to Get Started with Ajax and REST
The Easiest Way to Get Started with Ajax and REST Problem You want to know the best way to get started with writing Ajax and REST. Solution When developing an Ajax and REST application, you must decide on the tools and frameworks you’ll use. The choice is simple: Use whatever you’re using today, and write some Ajax applications. You don’t need to change the tools you’re using today. Whether you’re using ASP.NET, JavaServer Pages (JSP), PHP, Ruby, or Python, you...

6. Testing a Dynamic Contract with Ajax
Coding the Contract Using Test-Driven Development Techniques Coding the contract using agile and test-driven development techniques requires writing a number of tests and implementing aMock URL layer. Problem You want to code the contract using these development techniques. Solution To demonstrate, let’s define a use case, implement the use case as a contract, write a test case(s) to implement the contract, implement the contract in the Mock URL, and finally...

7. Testing the Client Side Logic
Problem You want to effectively test your application’s client-side logic. Theory Testing GUI code tends not to be a productive task because of the complications that arise. The main complication is how to test the correctness of a user interface. Imagine a situation where clicking a button causes a table to be filled with data. Now imagine that when a check box is checked and the button is clicked again, a different table is filled with content. The fact that clicking the same button results in two ...

8. Understanding JavaScript and Types
Understanding JavaScript and Types Problem You want to work around the fact that JavaScript does not have types declared for its variables. Theory JavaScript code does not have any variables with a declared type. The lack of typed variables is apparent when you declare functions. That said, not having typed variable declarations does not mean JavaScript has no types or no type safety. Let’s start out with the simple declaration of a function, as illustrated by the following ex...