Situation: In an AJAX application, there are several clickable objects that make AJAX requests. The user should be able to click objects without waiting for the previous AJAX call to finish and have all calls process.
What happens when an XHR call is made before a previous call is finished?
Of course, this depends on the requirements and how the AJAX is coded. There are three likely behaviors from which to choose.
Below is a quick example. Click the 4 calculations as fast as possible to cause the request to collide. The AJAX calls are simple with little server processing and may complete too soon so the "calculate" button is provided. It simulates rapidly clicking each calculation.
XHR Threads | Not Threaded |
---|---|
|
|
To have each request run in its own thread, a closure is used. A closure binds variables from a parent method's scope that would ordinarily be lost when execution of the method completed to a child function. (see references at page bottom)
Three functions are used (not including button functions for the examples): createXHR, showReturnedData, and runAjax.
createXHR is a standard, ubiquitous method to create an instance of the xmlHttprequest object. (See Ajax in a Nutshell: Create an xmlHttpRequest (XHR) Object for details.)
function createXHR() { var xhrObj; if (window.XMLHttpRequest) { // branch for native XMLHttpRequest object - Mozilla, IE7 try { xhrObj = new XMLHttpRequest(); } catch (e) { xhrObj = null; } } else if (window.createRequest) { /* For ICEbrowser -- untested. * per their site * http://support.icesoft.com/jive/entry.jspa?entryID=471&categoryID=21 */ try { xhrObj = window.createRequest(); } catch (e) { xhrObj = null; } } else if (window.ActiveXObject) { // branch for IE/Windows ActiveX version try { xhrObj = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try{ xhrObj = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { xhrObj = null; } }//catch } //if-else return xhrObj; }//eof createXHR
Next we have a simple function to handle the returned data. This can be as complex as the data requires, but, to demonstrate the concept, we can use a simple function.
function showReturnedData(text, targId) { var targ = document.getElementById(targId); targ.innerHTML = text; }//eof show
The important function is the last one. It is called to make the AJAX request and provides the threaded/concurrent capability. This can be adapted to attached to the event however you want. For demonstration purposes, the handler was attached in-line; i.e., onclick="runAjax(...);".
1 function runAjax(url, callback, targId) { 2 var xhr = createXHR(); 3 4 /* Inner function to bind handler and create closure */ 5 function bindCallback() { 6 if (xhr.readyState == 4) { 7 try { 8 if (xhr.status == 200) 9 callback(xhr.responseText, targId); 10 else 11 alert("Status error"); 12 } catch (e) { 13 //alert(e.message); 14 } 15 } 16 } 17 18 if (xhr) { 19 try { 20 xhr.open("GET", url, true); 21 xhr.onreadystatechange = bindCallback; 22 xhr.send(null); 23 } catch (e) { 24 //Handle error 25 } 26 } 27 } //eof runAjax
On line 2, a local instance of an XHR object is created. This would appear useless since the runAjax function will complete before the AJAX request finishes. But, on line 5, an inner function is declared. It has access to the parameters and local variables of the parent. They won't be destroyed until the inner function is no longer callable. When, on line 21, a reference to the inner function is assigned to the event on the XHR object, the callback function, its arguments, and the XHR object is closed over: the XHR object and variables remain available to the callback function, here showReturnedData, until the request is completed.
Each time the function runAjax is called a new XHR object and inner function are created with a new context object: closed over variables and values.
The built-in garbage collection routines will clean up the objects when the call has completed with some caveats. Closures risk causing memory leaks so care needs to be taken. For more information on closures checkout:
For more on similar AJAX methods: