XML.com: XML From the Inside Out

XML.comWebServices.XML.comO'Reilly Networkoreilly.com
  Articles | Weblogs | Newsletter | Safari Bookshelf
advertisement

Article:
 Very Dynamic Web Interfaces
Subject: A threadsafe implementation for XMLHTTPRequest
Date: 2005-05-27 12:48:37
From: brockweaver

One of the things that pops out at most developers is the thread safety issues of the example code. While the example works fine for testing, the very nature of programming web apps in this manner means it is *extremely* likely multiple requests will be issued concurrently.


The following is thread-safe code to do just that. I've used the Ajax moniker, as that is what a lot of people call this technique. Personally, I don't care if they call it furrycatofdeath, as long as it works. :)


Essentially, this code uses a technique called inner functions. These are useful in this situation because the variables declared on the outer function are available to the inner function -- and do not fall out of scope until *after* the inner function has been executed. Since the request and callback variables (which contain function pointers to the good stuff) are declared locally, each call to the ajaxSend function creates a new copy, so the previous one is left intact.


A great discussion and analysis of this methodology (and other similar methodologies) is described in detail at
http://jibbering.com/faq/faq_notes/closures.html


Mind you, I found this via a Google search and I am completely unrelated with its content, so that url may change without my knowledge. Hopefully it will not, as it is a very clear and well written article!


At any rate, the source follows. If you have issues with it, please let me know, as I haven't had a chance to test many platforms yet. Thank you!


// threadsafe asynchronous XMLHTTPRequest code
function ajaxSend(url, callback){


// we use a javascript feature here called "inner functions"
// using these means the local variables retain their values after the outer function
// has returned. this is useful for thread safety, so
// reassigning the onreadystatechange function doesn't stomp over earlier requests.


function ajaxBindCallback(){
if (ajaxRequest.readyState == 4) {
if (ajaxRequest.status == 200) {
if (ajaxCallback){
ajaxCallback(ajaxRequest.responseXML);
} else {
alert('no callback defined');
}
} else {
alert("There was a problem retrieving the xml data:\n" + ajaxRequest.status + ":\t" + ajaxRequest.statusText + "\n" + ajaxRequest.responseText);
}
}
}


// use a local variable to hold our request and callback until the inner function is called...
var ajaxRequest = null;
var ajaxCallback = callback;


// bind our callback then hit the server...
if (window.XMLHttpRequest) {
// moz et al
ajaxRequest = new XMLHttpRequest();
ajaxRequest.onreadystatechange = ajaxBindCallback;
ajaxRequest.open("GET", url, true);
ajaxRequest.send(null);
} else if (window.ActiveXObject) {
// ie
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
if (ajaxRequest) {
ajaxRequest.onreadystatechange = ajaxBindCallback;
ajaxRequest.open("GET", url, true);
ajaxRequest.send();
}
}


}




Previous Message Previous Message   Next Message Next Message


Titles Only Titles Only Newest First
  • A threadsafe implementation for XMLHTTPRequest
    2007-10-02 00:31:58 fiskus [Reply]

    Interesting.


    I came up with a very similar solution to this problem. (See http://badcoding.blogspot.com/2007/06/fun-with-ajax.html)


    (Yes, it was a month after you, but quite independently ;-)


    You appear to have assigned both the handler and the callback to local variables prior to issuing the call. However, I'm not sure whether passing the handler by reference like this is really thread safe in an interpretive language like javascript. (I may be wrong, but it was the impression I got when testing)


    I tried to avoid this problem by storing the handlers in a global array, and passing the array index to the callback function. This is definitely thread safe, although having a global array that grows like a stack of discarded dishes isn't a great idea, either!

  • A threadsafe implementation for XMLHTTPRequest
    2005-06-06 12:42:15 GoatMonkey [Reply]

    Can you explain how to get data back from this function with the inner function? If I wanted to use this function to send different URLs to it and get back either XML or plain text as a response in a variable.


    When I tried it, it seemed to work, but I had to modify the function to just write out the xml to the screen within the inner function. It seems like it should be returned as a variable to whereever it was called from.


    Thanks.

    • A threadsafe implementation for XMLHTTPRequest
      2005-08-05 09:08:18 js9777 [Reply]



      • A threadsafe implementation for XMLHTTPRequest
        2005-09-05 00:37:06 sillyxone [Reply]

        Thanks a lot for the excellent article, so simple but yet powerful. I took a little freedom to modify your codes for my own use (just added a function called handleResponse(), no xml used to simplify the example).


        [ajax.js]
        // threadsafe asynchronous XMLHTTPRequest code
        function handleResponse(response) {
        var sepInd = response.indexOf('|', 0);
        if(sepInd != -1) {
        updateDiv = response.substring(0, sepInd);
        updateCont = response.substring(sepInd + 1);
        //update = response.split('|');
        document.getElementById(updateDiv).innerHTML = 'Done';
        }
        }


        function ajaxSend(url, callback){
        // we use a javascript feature here called "inner functions"
        // using these means the local variables retain their values after the outer function
        // has returned. this is useful for thread safety, so
        // reassigning the onreadystatechange function doesn't stomp over earlier requests.


        function ajaxBindCallback() {
        if (ajaxRequest.readyState == 4) {
        if (ajaxCallback) {
        ajaxCallback(ajaxRequest.responseText);
        }
        else {
        alert('no callback defined');
        }
        }
        }


        // use a local variable to hold our request and callback until the inner function is called...
        var ajaxRequest = null;
        var ajaxCallback = callback;



        // bind our callback then hit the server...
        if (window.XMLHttpRequest) {
        // moz et al
        ajaxRequest = new XMLHttpRequest();
        ajaxRequest.onreadystatechange = ajaxBindCallback;
        ajaxRequest.open("GET", url, true);
        ajaxRequest.send(null);
        }
        else
        if (window.ActiveXObject) {
        // ie
        ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
        if (ajaxRequest) {
        ajaxRequest.onreadystatechange = ajaxBindCallback;
        ajaxRequest.open("GET", url, true);
        ajaxRequest.send();
        }
        }
        }




        [test.php]
        <?php
        if (isset($_GET['num'])) {
        echo "uDiv{$_GET['num']}|";
        for ($i = 0; $i < 999999; $i++) // use big data to create a delay in transfer
        echo "$i ";
        exit();
        }
        ?>
        <html>
        <head>
        <script language="javascript" src="ajaxs.js"></script>
        <script language="javascript">
        function updateMe(divNum) {
        ajaxSendG('test.php?num=' + divNum, handleResponse);
        }
        </script>
        </head>
        <body>
        <h2>Testing</h2>
        <?php
        for ($i = 0; $i < 5; $i++)
        print <<< END_BLOCK
        <input type="button" onClick="updateMe({$i})" value="Update Div {$i}">
        <div id="uDiv{$i}"></div>
        END_BLOCK;
        ?>
        </body>
        </html>


        • A threadsafe implementation for XMLHTTPRequest
          2005-09-05 01:12:48 sillyxone [Reply]

          sorry, there is a typo in the above codes, the src of the script should be "ajax.js" instead of "ajaxs.js". Again, thanks a lot for the article and the link to the explaination.

Sponsored By:


Contact Us | Our Mission | Privacy Policy | Advertise With Us | | Submissions Guidelines
Copyright © 2008 O'Reilly Media, Inc. | (707) 827-7000 / (800) 998-9938