Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Page 1 of 2 12 LastLast
Results 1 to 15 of 19
  1. #1
    New Coder
    Join Date
    Dec 2009
    Posts
    15
    Thanks
    1
    Thanked 0 Times in 0 Posts

    IE8 won't render ajax responses 1 at a timr

    I created a simple AJAX model of something I'm trying to do, which is loop through a list of numbers, sending an AJAX request with each iteration, and displaying the response text of each request as they complete. As though going through a bulk process, and keeping the user informed on what numbers have been processed so far.

    It works fine in Firefox, but not in IE8. In IE8, instead of displaying the lines of text one at a time as each item is completed, the page continues working for several seconds, and then when it's done renders all 10 lines of text at once. (Also IE8 seems to cache the page sometimes - can I prevent that somehow?) I'm using Coldfusion, but it's easy to interpret what it's doing. Below is the code for my page, callAjax.cfm:

    Code:
    <script>
    
    function ajax(url, n) {
      // native XMLHttpRequest object
      var objDiv = document.getElementById('myDiv');
      if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
        req.onreadystatechange = function() {ajaxDone(objDiv, n);};
        req.open("GET", url, false);
        req.send(null);
        // IE/Windows ActiveX version
      } else if (window.ActiveXObject) {
       		req = new ActiveXObject("Microsoft.XMLHTTP");
        	req.onreadystatechange = function() {ajaxDone(objDiv, n);};
        	req.open("GET", url, false);
        	req.send(null);
        }
    }
    
    function ajaxDone(objDiv, n) {
      // only if req is "loaded"
      if (req.readyState == 4) {
        // only if "OK"
        if (req.status == 200) {
          results = req.responseText;
    	  //alert(req.responseText);
    	  if (n == 1) {
    	  	objDiv.innerHTML = '';
    	  }
         objDiv.innerHTML = objDiv.innerHTML + results + '<br>';
    	 if (n == 10) {
    	 	objDiv.innerHTML = objDiv.innerHTML + '<br> All done.';
    	 }
        } 
      }
    }
    </script>
    
    <cfoutput>
    <!--- the div is created. IE8 wants it to be populated with something, else the div object will be null, hence the nbsp --->
    <div id="myDiv">&nbsp;</div>
    
    <!--- then populated via 10 ajax requests--->
    	<script>
    	<cfloop from="1" to="10" index="n">
    		ajax('ajaxPage.cfm?rnd=' + Math.random() + '&n=#n#', #n#);
    	</cfloop>
    	</script>
    </cfoutput>
    and this is the page that is called via ajax (ajaxpage.cfm)):

    Code:
    <!--- make it wait a little bit... --->
    <cfloop index="randomindex" from="1" to="200000" step="1">
        <cfset random=rand()>
    </cfloop>
    <!--- then output the text --->
    <cfoutput>Line #url.n# ---- random number is: #random#</cfoutput>
    Notice the commented out alert in the js on callAjax.cfm. I was using it to see what the response text is at that point in the function. In IE8, the alert box always comes up blank, except for a triangle icon with a "!" inside it. Which makes no sense to me - the readyState is 4, the status is 200, so the responseText should be there. And in any case, all 10 lines are rendered correctly, so it's receiving the responseText at some point.

    Is there something I have to do to my js code to make IE8 render each line one at a time, instead of waiting to render all of them all at once?

  • #2
    Senior Coder
    Join Date
    Apr 2011
    Location
    London, England
    Posts
    2,120
    Thanks
    15
    Thanked 354 Times in 353 Posts
    IE is a bit dim/slow on the uptake. Try introducing a very short delay between each request:

    Code:
        <script>
        <cfloop from="1" to="10" index="n">
            window.setTimeout(function () {
                ajax('ajaxPage.cfm?rnd=' + Math.random() + '&n=#n#', #n#);
            }, 50); // 50 ms
        </cfloop>
        </script>
    Oops, you will need to remember the current value of n for each call:
    Code:
        <script>
        <cfloop from="1" to="10" index="n">
            window.setTimeout(function () {
                var #thisN# = #n#;
                ajax('ajaxPage.cfm?rnd=' + Math.random() + '&n=#thisN#', #thisN#);
            }, 50); // 50 ms
        </cfloop>
        </script>
    but I don't know if this is the correct syntax for CF (just guessing).
    Last edited by AndrewGSW; 12-12-2012 at 10:01 PM.
    "I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
    Validate your HTML and CSS

  • #3
    New Coder
    Join Date
    Dec 2009
    Posts
    15
    Thanks
    1
    Thanked 0 Times in 0 Posts
    Hm, that didn't change anything, unfortunately; still the same behavior.

    A couple small things I discovered though:

    1) Since I'm getting synchronously (3rd argument in get() is "false"), it's not necessary to define a function for onreadystatechange; I can just put the code in ajaxDone() right after the send() line.

    2) alert(req.responseText) -- the responseText is there, it just had so much whitespace that the alert box was stretching down below the window and the text was out of view. If I trim the response text, the alert box shows it just fine.

  • #4
    Master Coder felgall's Avatar
    Join Date
    Sep 2005
    Location
    Sydney, Australia
    Posts
    6,640
    Thanks
    0
    Thanked 649 Times in 639 Posts
    Have you tried using the call asynchronously the way it is supposed to be used. The problem could be because you are calling it synchronously.
    Stephen
    Learn Modern JavaScript - http://javascriptexample.net/
    Helping others to solve their computer problem at http://www.felgall.com/

    Don't forget to start your JavaScript code with "use strict"; which makes it easier to find errors in your code.

  • #5
    New Coder
    Join Date
    Dec 2009
    Posts
    15
    Thanks
    1
    Thanked 0 Times in 0 Posts
    Quote Originally Posted by felgall View Post
    Have you tried using the call asynchronously the way it is supposed to be used. The problem could be because you are calling it synchronously.
    Yes, but if I do that, it doesn't work in either FF or IE8. It displays the first then, then quits. I'm not positive as to why, but I think something like this needs to be synchronous, since each request is supposed to made and its response displayed in the order called. But then again, in the cleaner, simpler example I wrote below, I don't really care about the order displayed, so I'm a little unclear on why it can't be asynchronous.

    I've stripped down the code some and wrote an even simpler example of this issue; here I don't feed any arguments to the ajax function. It just makes a call to a page that generates a random number and then it displays that number on the screen. I loop through and call it 10 times. Again, FF displays the lines 1 at a time as I want, but IE8 waits until all requests have been made and only then displays all 10 lines at once.

    callAjax3.cfm:
    Code:
    <script>
    var req;
    
    function ProcessAjax(url) {
      // IE8 - has native  object
      req = new XMLHttpRequest();
      req.onreadystatechange = function() {displayTheLine();};
      req.open("GET", url, false);
      // null for GET with native object
      req.send(null);
    }
    
    function displayTheLine() {
      if (req.readyState == 4 && req.status == 200) {
        var results = fncTrim(req.responseText);// take off any whitespace from the response
    	var theDiv = document.getElementById('theDiv');
        theDiv.innerHTML = theDiv.innerHTML + results;
      }
    }
    
    function fncTrim(str) {
    	str = str.replace(/^\s+|\s+$/g,'');
    	return str;
    }
    </script>
    
    <div id="theDiv">Here come 10 random numbers...<br></div>
    
    <cfloop from="1" to="10" index="i">
    	<script>
    		ProcessAjax('createRandomNumber.cfm?rnd=' + Math.random());
    	</script>
    </cfloop>
    and the page that is called, createRandomNumber.cfm:
    Code:
    <cfsetting enablecfoutputonly="yes">
    <cfscript>
    	sleep(1000);//delay by 1 second to simulate a longer process
    </cfscript>
    <cfset random=rand()>
    <cfoutput>random number is: #random#<br></cfoutput>

  • #6
    Senior Coder
    Join Date
    Apr 2011
    Location
    London, England
    Posts
    2,120
    Thanks
    15
    Thanked 354 Times in 353 Posts
    It displays the first then, then quits. I'm not positive as to why
    If you are using asynchronous with the same (global) request-object then, as I understand, only the first returned-response will be acted upon.

    If synchronous then I still suggest creating a slight delay between requests will help with IE - but I may be wrong . [The sleep() response is different as all the requests will stack-up, but return in quick succession after 1 second.]
    "I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
    Validate your HTML and CSS

  • #7
    New Coder
    Join Date
    Dec 2009
    Posts
    15
    Thanks
    1
    Thanked 0 Times in 0 Posts
    Quote Originally Posted by AndrewGSW View Post
    If you are using asynchronous with the same (global) request-object then, as I understand, only the first returned-response will be acted upon.
    With this example, I tried at first making the request object *not* global, defining it inside the function:



    Code:
    <script>
      function processAjax(url) {
         var req = new XMLHttpRequest();
          ...
          req.onreadystatechange = function() {displayTheLine();};
          ...
    But then I got an error message indicating that displayTheLine() doesn't recognize any variable named "req". Not sure what the encapsulation rules are here, but at first I thought that req would be available to displayTheLine(). When it didn't work, I tried defining req globally. To make it work async, Should I still define req inside
    of ProcessAjax(), but without the "var"? Or should I keep the "var" and feed req to displayTheLine() as an argument? (i.e., req.onreadystatechange = function() {displayTheLine(req);};
    Or do something else entirely?

  • #8
    Master Coder felgall's Avatar
    Join Date
    Sep 2005
    Location
    Sydney, Australia
    Posts
    6,640
    Thanks
    0
    Thanked 649 Times in 639 Posts
    Why not just wrap your entire code inside an anonymous self executing function. That way any variables you declare will be limited to that function and will not be global - but they will be available to any functions defined inside the anonymous one.

    Don't get rid of the var - once you start writing strict javaScript all variables MUST be declared or the script will crash with a reference error.

    I'd also pass req into the function as a parameter since the function needs it.
    Stephen
    Learn Modern JavaScript - http://javascriptexample.net/
    Helping others to solve their computer problem at http://www.felgall.com/

    Don't forget to start your JavaScript code with "use strict"; which makes it easier to find errors in your code.

  • #9
    Senior Coder
    Join Date
    Apr 2011
    Location
    London, England
    Posts
    2,120
    Thanks
    15
    Thanked 354 Times in 353 Posts
    that might look like this:
    Code:
    <cfloop from="1" to="10" index="i">
    	<script>
    		(function (req) {
                         // all ajax code here..
                    }) (new XMLHttpRequest());
    	</script>
    </cfloop>
    "I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
    Validate your HTML and CSS

  • #10
    Senior Coder
    Join Date
    Apr 2011
    Location
    London, England
    Posts
    2,120
    Thanks
    15
    Thanked 354 Times in 353 Posts
    Something like this:
    Code:
    <cfloop from="1" to="10" index="i">
    	<script>
            (function (req) {
                req.onreadystatechange = function () {
                    if (req.readyState == 4 && req.status == 200) {
                        var results = fncTrim(req.responseText);
                        var theDiv = document.getElementById('theDiv');
                        theDiv.innerHTML = theDiv.innerHTML + results;
                    }
                };
                req.open("GET", 'createRandomNumber.cfm?rnd=' + Math.random(), true);
                req.send(null);
            }) (new XMLHttpRequest());
            
    	</script>
    </cfloop>
    but untested, soz. Yeah, works for me (in IE etc.)
    Last edited by AndrewGSW; 12-14-2012 at 08:26 PM.
    "I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
    Validate your HTML and CSS

  • #11
    Senior Coder
    Join Date
    Apr 2011
    Location
    London, England
    Posts
    2,120
    Thanks
    15
    Thanked 354 Times in 353 Posts
    To work with <= IE8 might be:
    Code:
    <cfloop from="1" to="10" index="i">
    	<script>
            (function (req) {
                req.onreadystatechange = function () {
                    if (req.readyState == 4 && req.status == 200) {
                        var results = fncTrim(req.responseText);
                        var theDiv = document.getElementById('theDiv');
                        theDiv.innerHTML = theDiv.innerHTML + results;
                    }
                };
                req.open("GET", 'createRandomNumber.cfm?rnd=' + Math.random(), true);
                req.send(null);
            }) (new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP"));
            
    	</script>
    </cfloop>
    "I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
    Validate your HTML and CSS

  • #12
    Senior Coder
    Join Date
    Apr 2011
    Location
    London, England
    Posts
    2,120
    Thanks
    15
    Thanked 354 Times in 353 Posts
    Here are test pages if anyone might be interested in testing this:
    testAjax.html
    Code:
    <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
    <script type="text/javascript">
        
    function fncTrim(str) {
    	return str.replace(/^\s+|\s+$/g,'');
    }
    
    for (var i=0; i < 10; i++) {
        (function (req) {
            req.onreadystatechange = function () {
                if (req.readyState == 4 && req.status == 200) {
                    var results = fncTrim(req.responseText);
                    var theDiv = document.getElementById('theDiv');
                    theDiv.innerHTML = theDiv.innerHTML + results;
                }
            };
            req.open("GET", 'createRandomNumber.php?rnd=' + Math.random(), true);
            req.send(null);
        }) (new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP"));
    }
    
    </script>
    </head>
    <body>
    <h1>10 Asynchronous Ajax Requests</h1>
    <p>Here come the 10 random numbers..</p>
    <div id="theDiv">Wait..<br></div>
    
    </body>
    </html>
    createRandomNumber.php
    PHP Code:
    <?php
    sleep
    (1);   // 1 second
    echo rand(1100) . "<br>";
    ?>
    The data comes back or, perhaps more accurately, is rendered in chunks(?); sometimes 1-2-1-2..
    Last edited by AndrewGSW; 12-14-2012 at 10:15 PM.
    "I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
    Validate your HTML and CSS

  • #13
    New Coder
    Join Date
    Dec 2009
    Posts
    15
    Thanks
    1
    Thanked 0 Times in 0 Posts

    Problem is, if open() is synchronous, IE delays rendering

    First, I want to let everyone know I've decided to get medieval on this problem and I've posted a job for it in Elance, so if anyone knows Coldfusion and Ajax and feels like making a little cash, the job's titled "Ajax + Coldfusion Problem - IE8 Delays Rendering Response Text".

    Second, I've figured out I pretty much have to do this synchronously, so the main problem is that I need IE to do what FF is doing: a line for each request appears on the screen as soon as the response text is received.

    I've posted the exact code I'm using below, because I've taken one of the recent examples and modified things just a bit. The called page no longer returns a random number; instead, I'm doing something closer to what I want to accomplish, which is pass an "ID" to the page via ajax, and have the page feed that ID to a function. In this case, the "function" simply adds 42 to it.

    Synchronous vs. Asynchronous:

    async: Both IE and FF render groups of lines, sporadically. That would be okay if the order in which the IDs are processed didn't matter. The user would still get to watch the progress, instead of staring at a blank screen until the whole batch is complete. It would also shorten the time it takes to do the whole batch, assuming the Coldfusion server could handle processing several IDs at a time; the real-life function I want to use is a good bit more involved. But in this case, the IDs will most likely need to be processed in a sequential order (and if not, I'm sure that a project with that requirement will eventually come up, so best to figure out the solution now).

    sync: If sync, FF will immediately render a line for each ID as soon as it's been processed and the response text is received, which is the behavior I want. But again, IE instead waits until all requests have been processed, and only then renders all the lines, at once. So the user is left staring at a blank screen until all the IDs have been processed, and that's exactly what I don't want.

    CallAjax6.cfm:
    Code:
    <script>
    	function fncTrim(str) {
    		str = str.replace(/^\s+|\s+$/g,'');
    		return str;
    	}
    </script>
    
    <div id="theDiv">Now Processing IDs...<br><br></div>
    
    <cfloop from="1" to="10" index="i">
    	<script>
            (function (req) {
                req.onreadystatechange = function () {
                    if (req.readyState == 4 && req.status == 200) {
                        var results = fncTrim(req.responseText);
                        var theDiv = document.getElementById('theDiv');
                        theDiv.innerHTML = theDiv.innerHTML + results;
                    }
                };
                req.open("GET", 'processID.cfm?id=<cfoutput>#i#</cfoutput>', true);
                req.send(null);
            }) (new XMLHttpRequest());
    	</script>
    </cfloop>
    ProcessID.cfm:
    Code:
    <cfsetting enablecfoutputonly="yes">
    <cfheader name="expires" value="-1"> 
    <cfheader name="pragma" value="no-cache"> 
    <cfheader name="cache-control" value="no-cache, no-store, must-revalidate">
    
    <cfparam name="url.id" default="0">
    <!--- not sure if these cfheaders are necessary --->
    <cfscript>
    	sleep(2000);//delay by 2 seconds to simulate a longer-running process.
    	processID = url.id + 42;
    </cfscript>
    
    <cfoutput>ID #url.id# has been processed. The returned value is #processID#<br></cfoutput>

  • #14
    New Coder
    Join Date
    Dec 2009
    Posts
    15
    Thanks
    1
    Thanked 0 Times in 0 Posts

    Aha.

    I've continued to scour the web, and it seems this is a problem that pops up again and again for folks using synchronous XMLHttpRequests with IE, and this article best explains why: http://www.blacksquare.co.za/blog/?p=458.

    In a few words, IE intentionally delays rendering layout changes until the entire javascript thread has completed. If you use AJAX synchronously (or SJAX, I should say), the return processing is done on the same thread, so the rendering is delayed until after the response has been processed.

    And so the solution, according to this blog post, is simple - don't do it. Use AJAX asynchronously, which, so I keep hearing, is the ideal approach 99% of the time.

    So now I'm thinking of taking a different approach. Do the Ajax calls asynchronously, so now rendering is not dependent on a single thread. But rather than appending the innerHTML of a single div with each ajax response as it comes (which all but assures that everything will render out of order), set up some kind of structure beforehand, like a table, and give the TD tags ids that associate them with the ID numbers being processed. The top row's TD has id="1", and the 2nd row's TD has id="2", and so on.

    So if I have 10 IDs, the user initially sees a table with 10 rows, each of which read "processing..." or something like that, and then they're populated with their respective response text as the ajax responses come. It won't matter in what order the requests are responded to, because the table is already set up in order. When the batch is done, all the IDs' responses will appear in order.

    Fortunately, the app I'm working on already has a page that does something kind of similar using ajax. I'll post the code for a working model once I dig into it a little more.

  • #15
    Senior Coder
    Join Date
    Apr 2011
    Location
    London, England
    Posts
    2,120
    Thanks
    15
    Thanked 354 Times in 353 Posts
    Giving the DIV (or table cells) a defined width and height will make the rendering smoother. The content could be inserted using DOM methods, rather than innerHTML, but the impact of this change is going to be minimal.

    Rather than inserting the content directly it could be stored in a string or array. A counter would be needed to determine when all values are received. Then setInterval could be used to insert the values.
    "I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
    Validate your HTML and CSS


  •  
    Page 1 of 2 12 LastLast

    Posting Permissions

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