• No results found

The Magic of the Asynchronous Class

Let’s focus on how the Asynchronous class solves the instance and callback problem. The specific code is illustrated again as follows:

function Asynchronous_call(url) { var instance = this;

this._xmlhttp.open('GET', url, true);

this._xmlhttp.onreadystatechange = function() { switch(instance._xmlhttp.readyState) { case 1: instance.loading(); break; case 2: instance.loaded(); break; case 3: instance.interactive(); break; case 4: instance.complete(instance._xmlhttp.status, instance._xmlhttp.statusText, instance._xmlhttp.responseText, instance._xmlhttp.responseXML); break; } } this._xmlhttp.send(null); }

Asynchronous_call is associated with an instance of Asynchronous because of the prototype

definition. Then when the HTML code calls asynchronous.call, the function Asynchronous_call is called and the this instance references the instantiated class. The variable this.xmlhttp is an instance of XMLHttpRequest, and the property onreadystatechange needs to be assigned a function. There is a peculiarity with JavaScript in that if a property is assigned the value of this.somefunction, then what is assigned is a function and not a function associated with a class instance, as was shown by the code that looked like it would work, but didn’t.

When the method Asynchronous_call is called, the this variable references an instance of

Asynchronous. What is happening is that JavaScript is associating a function with an instance. Logically then, if the property onreadystatechange were assigning a function associated with an instance of Asynchronous, then when a callback is made, the this variable should reference an instance of Asynchronous. Figure 2-7 shows that there is no reference to an instance of

Figure 2-7. Debugger illustrating that a function does not reference a class instance

The debugger shown in Figure 2-7 is distributed with Mozilla, and in the middle window on the left side is a reference to the this variable. The watch window illustrates that this does not reference an instance and is a plain, simple ScriptFunction. This means that even though the original function was associated with an instance of Asynchronous, when used as a callback the reference disappears.

A solution would be to cross-reference a request with an Asynchronous instance that is stored in an array that is accessed to identify the request. Such a solution is complicated and relies on some global array.

The solution is not a complex cross-referencing algorithm, but the use of a unique imple- mentation detail of JavaScript. Look back at the implementation of Asynchronous_call, illustrated briefly as follows:

function Asynchronous_call(url) { var instance = this;

this._xmlhttp.open('GET', url, true);

this._xmlhttp.onreadystatechange = function() {

First, the this variable is assigned to the instance variable The assignment is important because it is a variable that is managed by JavaScript. Second, the property onreadystatechange

is assigned a dynamic anonymous function. An anonymous function is a function without an identifier, which contains only a signature and implementation. Using an anonymous function in the context of a function allows the referencing of variables in the anonymous function that were defined in the function itself. This means the variable instance is available for referencing in the anonymous function. What makes this feature a big deal is that when the anonymous function is called, the caller of Asynchronous_call will already have exited the function and be doing something else. The reason the local variable instance is still available is because JavaScript sees a reference and does not garbage-collect it until the this_xmlhttp instance is garbage- collected.

Putting all of this together in the HTML code, the Asynchronous property complete is assigned the functions AsyncUpdateEvent and AsyncUpdateEvent2. Whenever any of these functions are called, the this references a valid instance of Asynchronous. Then the code that was referencing

myState, which should have worked, would work. Looking at the HTML code, you can see that the

AsyncUpdateEventthis references the variable asynchronous, and AsyncUpdateEvent2this refer- ences the variable asynchronous2. Figure 2-8 shows the proof that the this variable is assigned.

In Figure 2-8 the debugger shows that this references an instance of Asynchronous. In the example HTML code, the methods AsyncUpdateEvent and AsyncUpdateEvent2 do not use the

this variable, but they could.

Now you’re ready to put it all together and execute the HTML code. Click the Get a Document button and then click the Get a Document2 button. The HTML page in Figure 2-9 is generated.

Figure 2-9. HTML page state after immediate feedback of the second row

In Figure 2-9 the second row contains data, whereas the first row does not. This is because the second row references a static document that is downloaded and processed immediately. The first row is not yet filled out because there is a 10-second delay. After 10 seconds, the HTML page appears similar to Figure 2-10.

In Figure 2-10 the page is in its final state with both rows containing data. The processing occurred at different times, and the two requests ran concurrently. Using the defined Asynchronous

class, multiple requests could be running at the same time.