XMLHttpRequest and Javascript Closures

Following from Simon’s excellent tips on Closures and executing JavaScript on page load got an email from Scott Raymond regarding how this can be applied to XMLHttpRequest so figured I’d share some experiences I’ve been having as a result of ScriptServer.

The Mozilla implementation of XMLHttpRequest provides the “onload” and “onerror” properties to which you can assign your own callbacks, the callback automatically being passed an Event object through which you can get back to your calling object – this is best seen by looking at the Mozblog nsXmlRpcClient.js – see what they do with their _onload function.

Unfortunately, Microsoft’s ActiveX implementation doesn’t, requiring you use a property called onreadystatechange and thankfully Mozilla also supports this (side note – is Mozilla’s XMLHttpRequest the first time someone’s pulled Microsoft’s favorite “Embrace and Extend” on Microsoft themselves? ). The problem is nothing get’s automatically passed to the callback function assigned to onreadystatechange, but that’s where the closure comes in handy.

The rest I’ll leave to a long piece of code

   <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <script type="text/javascript"> <!-- // Mozilla only implementation!  // Constructor for generic HTTP client function HTTPClient() {};  // Add methods and properties as array HTTPClient.prototype = {     url: null,      // Instance of XMLHttpRequest     xmlhttp: null,      // Used to make sure multiple calls are not placed     // with the same client object while another in progress     callinprogress: false,      // The user defined handler - see MyHandler below     userhandler: null,      init: function(url) {         this.url = url;         this.xmlhttp = new XMLHttpRequest();     },      // handler argument is a user defined object to be called     asyncGET: function (handler) {          // Prevent multiple calls         if (this.callinprogress) {             throw "Call in progress";         };          this.userhandler = handler;          // Open an async request - third argument makes it async         this.xmlhttp.open('GET',this.url,true);          // Have to assign "this" to a variable - not sure why can't use directly         var self = this;          // Assign a closure to the onreadystatechange callback         this.xmlhttp.onreadystatechange = function() {             self.stateChangeCallback(self);         }          // Send the request         this.xmlhttp.send(null);     },      stateChangeCallback: function(client) {         switch (client.xmlhttp.readyState) {              // Request not yet made             case 1:                 try {                     client.userhandler.onInit();                 } catch (e) { /* Handler method not defined */ }             break;              // Contact established with server but nothing downloaded yet             case 2:                 try {                     // Check for HTTP status 200                     if ( client.xmlhttp.status != 200 ) {                         client.userhandler.onError(                             client.xmlhttp.status,                             client.xmlhttp.statusText                             );                          // Abort the request                         client.xmlhttp.abort();                          // Call no longer in progress                         client.callinprogress = false;                     }                 } catch (e) {                     /* Handler method not defined */                 }             break;              // Called multiple while downloading in progress             case 3:                 // Notify user handler of download progress                 try {                     // Get the total content length                     // -useful to work out how much has been downloaded                     try {                         var contentLength =                              client.xmlhttp.getResponseHeader("Content-Length");                     } catch (e) {                         var contentLength = NaN;                     }                       // Call the progress handler with what we've got                     client.userhandler.onProgress(                         client.xmlhttp.responseText,                         contentLength                     );                  } catch (e) { /* Handler method not defined */ }             break;              // Download complete             case 4:                 try {                     client.userhandler.onLoad(client.xmlhttp.responseText);                 } catch (e) {                     /* Handler method not defined */                 } finally {                     // Call no longer in progress                     client.xmlhttp.callinprogress = false;                 }             break;         }     } }  // A user defined handler to response to the XMLHTTPRequest var MyHandler = {     onInit: function() {         echo("About to send request<br>");     },     onError: function(status,statusText) {         echo("Error: "+status+": "+statusText+"<br>");     },     onProgress: function(responseText,length) {         echo("Downloaded "+responseText.length+" of "+length+"<br>");     },     onLoad: function(result) {         echo("<pre>"+result+"</pre>");     }, }  // Just a function to help display results function echo(string) {     document.getElementById("results").innerHTML += string; }  // Invoke the client function getPage() {     // Modify this to some page     var url = "http://localhost/test/test.txt";     var client = new HTTPClient();     client.init(url);          try {         client.asyncGET(MyHandler);     } catch (e) {         alert(e);     }     echo("Async request so still able to do stuff here<br>"); } </script> </head> <body> <a href="javascript:getPage();">getPage</a> <div id="results"> </div> </body> </html>    Note I'm also using an asychronous request here - have heard people complain XMLHttpRequest is too slow, locking up the browser - an asychronous request helps you prevent that.

Note also this is also Mozilla specific right now. Since versions 3.x of the ActiveX XMLHttpRequest it seems, for some reason I don't understand, they've stopped populating properties like "status" until we hit readyState 4 - that means you can't respond to errors or get information about the request until it's completely finished. For a cross browser implementation of something similar, see here  (have yet to implement the closure though, hence using lots of globals) - example use here.


Category: programming Time: 2004-05-26 Views: 0

Related post

iOS development

Android development

Python development

JAVA development

Development language

PHP development

Ruby development


Front-end development


development tools

Open Platform

Javascript development

.NET development

cloud computing


Copyright (C) avrocks.com, All Rights Reserved.

processed in 0.135 (s). 12 q(s)