www.webdeveloper.com
Results 1 to 9 of 9

Thread: How can I simulate an XMLHttp synchronous request with an asynchronous one.

  1. #1
    Join Date
    May 2012
    Posts
    5

    How can I simulate an XMLHttp synchronous request with an asynchronous one.

    We have this legacy web application that populates the DOM from an ActiveX XMLHttp request. It was originally written around the turn of the century and it's showing it's age. If I can get through this I'll (hopefully) have the chance to rewrite this so that it's a little more manageable.

    We're running into problems when we use IE9 to access the SSL version of our site. All of our requests are synchronous but in IE9 weíre getting errors saying that a particular piece of data doesnít exist when we subsequently access the DOM. It appears that itís continuing to execute code before the DOM is finished loading. Our interim solution was to introduce a 10 second delay before accessing the request. Itís a hack but for the most part it seems to work.

    Iíd like to use the readystate to figure out if itís finished loading which means moving the request from synchronous to asynchronous execution. Iíve searched through the forums but I havenít found an example of this yet. That being said I'm just trying to figure out when it's finished loading.

    Does anyone know of a method to simulate a synchronous XMLHttp request using an asynchronous request? The people here have been trying to fix this issue for years with poor results.

    Thanks in advance,

    j

  2. #2
    Join Date
    May 2012
    Posts
    5
    Oh and this is a POST not a GET. The XML can be quite large at times I'm afraid (several megs)

  3. #3
    Join Date
    Jan 2007
    Location
    Wisconsin
    Posts
    2,120
    Best practice is to split your method into two around the call. So, where you may have:

    PHP Code:
    var doStuff = function() {
      
    // do some stuff

      // the asynchronous call you wish to treat "synchronously"
      
    var result post(urlparams);

      
    // do some more stuff
    // doStuff() 

    You'd rewrite like so:
    PHP Code:
    var doStuff = function() {
      
    // do some stuff

      // the third parameter is a callback
      
    var result post(urlparams, function() { doStuffContinue.apply(thisarguments) });
    // doStuff()

    var doStuffContinue = function() {
      
    // do some more stuff
    // doStuff() 

    Though, because of the nature of this, you may end up having to write it like so:
    PHP Code:
    var doStuff = function() {
      
    // do some stuff

      // the third parameter is a callback
      
    var that this;
      var 
    result post(urlparams, function() { doStuffContinue.apply(thatarguments) });
    // doStuff()

    var doStuffContinue = function() {
      
    // do some more stuff
    // doStuff() 
    Jon Wire

    thepointless.com | rounded corner generator

    I agree with Apple. Flash is just terrible.

    Use CODE tags!

  4. #4
    Join Date
    May 2012
    Posts
    5
    Hi Jon,

    Thanks for your reply. I'm a little confused though. I didn't think JavaScript had a straight 'post' command. Here's (roughly) what's happening now:

    Code:
    // method = 'POST', async now false but I want to make it true
    var myFunction = function (method, url, async, uname, upass, content)
    {
    	var http = new ActiveXObject("msxml2.xmlhttp" + XMLLIB_VERSION);
    	http.open(method, url, obj.m_async, obj.m_uname, obj.m_upass);
    	http.send(content);
    	if (http.statusText == 200)
    		{...}
    }
    I thought I would have to have created a function to attach to the onreadystatechange of the XMLHttp request. I'm using straight JavaScript. (Well technically JScript I guess since it's IE only.)

    j

  5. #5
    Join Date
    Jan 2007
    Location
    Wisconsin
    Posts
    2,120
    Right. post() is a standing for whatever you're doing to assemble and make your request. So, that does means you have to pass the callback method down the chain. And, in my example, I'm just assuming the post() method returns the request itself, and the remaining portion of the code operates on the completed request object, accessing the responseText, responseXML, or response headers as necessary.

    So, you might implement post() like this:

    PHP Code:
    var post = function (urldatacallbackuserpass) {
      var 
    request getXMLHttpRequest();
      
    request.onreadystatechange = function() {
        if (
    request.readystate == 4) {
          
    callback();
        }
      }
      
    request.open("POST",url,true,user,pass);
      
    request.send(data);
      return 
    request;
    // post()


    var getXMLHttpRequest = function () {
      if (
    window['XMLHttpRequest']) {
        return new 
    XMLHttpRequest();
      } else {
        return new 
    ActiveXObject("Microsoft.XMLHTTP");
      }
    //getXMLHttpRequest 
    Or something like that ... You'll have to test and tweak it yourself, of course. But, I think this is the concept you're looking for. Let me know if I've misunderstood, of course.
    Jon Wire

    thepointless.com | rounded corner generator

    I agree with Apple. Flash is just terrible.

    Use CODE tags!

  6. #6
    Join Date
    Jul 2008
    Location
    urbana, il
    Posts
    2,787
    here is a magic jabvascript function that turns async code into sync-style code.

    i threw a demo in too, so you know how to actually use it.

    Code:
    function Sentry(){ 
      var args=arguments, ar=[].slice.call(args[0]);
      var caller=args.callee.caller;
      var str=caller.toString();
      var pc=str.split(/\bendSentry\b/)[0].split(/\bnew Sentry\b/)[1].split(/\bcontinueSentry\b/);
      var tail="function tail(){"+caller.toString().split("endSentry;")[1].trim();//
      var left=pc.length;
    
      var head=str.split(/return\s+new\s+Sentry/)[0]+";var _left="+left+";";
       head=head.replace(/function\s+([\w_$]+)/g,"function _sentry_$1");
      var fnName=head.split("(")[0].split(/function\s+/)[1];
      var lut={}, wr;
      if((wr=str.split(/\bendSentry\b/)).length>2){
    	throw new TypeError( "Sentry: duplicate 'endSentry' keyword detected in "+caller.name+"() at line #"+ (wr.slice(0,2).join(" ").split("\n").length).toString()); 
      }
      var pc2=str.toString().replace("new Sentry(arguments)","new Sentry").split(/\bendSentry\b/)[0].split(/\bnew Sentry\b/)[1].split(/\bcontinueSentry/);
      var myCBs=pc.map(function(a,b,c){
        var varName=a.split(/Sentry\(([^\)]+)\)/)[1];
        var cmd=varName;
        var newCB=String("function _sentry_"+varName+"(value){"+varName+"=value; "+
          "var sentryOb="+fnName+".sentry.callBacks['"+varName+"']; sentryOb.expired=true;sentryOb.runtime=(sentryOb.finished=+new Date)-sentryOb.born; if(!--_left){tail();}}");
        var ret= {name: varName, cb: newCB, born: +new Date, expired: false};
       return lut[varName]=ret;
      });
     var bod=pc2.map(function(a){return a.replace(/Sentry\(([\w_$]+)\)/g, function(j,nam){ return lut[nam].cb; }); }).join("\n");
     var myCode=[head, bod,tail,"}"].join("\n");
     var myFN=eval("0||"+myCode);
     var out={complete:tail, callBacks: lut, count: left, caller: caller.name+"("+ar+")", born:+new Date };
       myFN.sentry=out;
       myFN.apply();
     return out;
    }//end Sentry()
    Demo usage:
    Code:
    //normal async ajax function:
    function aGet(turl, callback) {
    	var XHRt=new XMLHttpRequest;
    	XHRt.onreadystatechange = function () {if (XHRt.readyState == 4 && XHRt.status == 200) {callback(XHRt.responseText, XHRt);}};
    	XHRt.open("GET", turl, true);
    	XHRt.send("");
    	return XHRt;
    };window.aGet=aGet;
    
    
      // helper function: plucks the title from an html page's string source:
      function getTitle(strHTML){ 
         return strHTML.split("</title>")[0].split("<title>").slice(-1)[0]; 
      }
    
    //demo of sync syntax in a magic ajax function:
    function getSomePages(pageURL){
      var page, site; //defines placeholders for async operation returns
    
      //install sentry by returning an invocation BEFORE you do anything else:
       return new Sentry(arguments);
    
     //fetch the two resources using async ajax, building a memorization callback using Sentry(varName) syntax.
        aGet(".", Sentry(page) );
       continueSentry;  //this keyword goes between request #1 and request # (total-1)
    
        aGet("/",  Sentry(site) );
      endSentry; //this keyword goes after all magic dispatches to sentry
    
      //use the async-defined vars as though you know them already:
       alert(   getTitle(page)+" |||| "+getTitle(site) );
    
    } 
    
    getSomePages(".")
    without comments, you can see how clean the former async code turns into sync-like procedural style simpleness:
    Code:
    function getSomePages(pageURL){
      var page, site; 
    
       return new Sentry(arguments);
    
       aGet(".", Sentry(page) );
        continueSentry;  
    
       aGet("/",  Sentry(site) );
        endSentry; 
    
       alert(   getTitle(page)+" |||| "+getTitle(site) );
    
    }
    Last edited by rnd me; 05-05-2012 at 04:07 PM.

  7. #7
    Join Date
    May 2012
    Posts
    5
    Hi Jon,

    This looks to be almost what I need. The function I'm using though is in the middle of a stack of other functions so although it does execute the doStuffContinue after it gets it the script keeps running.

    In order for this to work there can't be any code that goes after the post can there?

    j
    p.s. The xml coming back is not a page per se. Just data that will be used to fill the page.
    Last edited by Jason Cameron; 05-07-2012 at 06:50 AM.

  8. #8
    Join Date
    May 2012
    Posts
    5
    Hi rnd_me.

    I'm trying out your code. It's pretty complex. (not sure how to delete a post unfortunately)

    Thanks in advance,

    j
    Last edited by Jason Cameron; 05-07-2012 at 07:05 AM.

  9. #9
    Join Date
    Jan 2007
    Location
    Wisconsin
    Posts
    2,120
    Correct. Using the "best-practice" approach, anything and everything that needs to be executed after the request needs to be executed with a callback. With that in mind, you just need to treat the function containing post() as an asynchronous function and repeat the pattern in the container:

    PHP Code:
    function a() {
      
    // do stuff
      
    var that this;
      
    b(p1p2, function() { a_continue.apply(that); });
    }

    function 
    a_continue() {
      
    // do more stuff
    }

    function 
    b(p1p2callback) {
      
    // do stuff
      
    var that this;
      
    post(urlparams, function() { b_continue.apply(that); });
    }

    function 
    b_continue() {
      
    // do more stuff. execute callback, if necessary.
      
    if (callback && typeof(callback) == 'function') {
        
    callback();
      }

    And of course, anything containing the container needs to work similarly.
    Jon Wire

    thepointless.com | rounded corner generator

    I agree with Apple. Flash is just terrible.

    Use CODE tags!

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center



Recent Articles