#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2012
    Posts
    43
    Rep Power
    4

    Ajax instead of cfflush?


    Using CF8 - I have an action page that loops through a list of IDs and plugs them into a CF function. This process can take some time depending on the number of IDs, so I want to output a line of text to the browser (IE8) each time an ID has been processed, so the user isn't sitting there staring at a blank screen.

    ID 1 has been processed.
    ID 2 has been processed.
    ...
    ID n has been processed.

    Problem is, cfflush is terrible for this; I've played with the interval attribute, but it will wait for a while then output 3 1/2 lines, then wait a while again and output 2 lines, etc. Instead, I want 1 complete line and only 1 complete line to appear at a time, as soon as the function has processed the ID and returned something. So I'm not going to use cfflush.

    I've researched the web and played with Ajax to try to get this to happen (I can't use jquery because our servers don't have the libraries on them), but I don't seem to be doing it correctly. Can someone give me a basic idea in pseudocode of what I want to do, or point me to a url that explains how to do this in AJAX?
  2. #2
  3. No Profile Picture
    Moderator

    Join Date
    Jun 2002
    Location
    Raleigh, NC
    Posts
    5,281
    Rep Power
    968
    There probably isn't an "example" of this that you can look at, but what you're talking about is creating a queue. So you'd send a "start" request to the server to initiate the process. You'd then have JavaScript on the client that polls the server to check the status of the queue and display the current status. Unfortunately, it's not really a simple thing to do.
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2012
    Posts
    43
    Rep Power
    4
    Thanks much for that explanantion - I figured it wouldn't be simple, but that's certainly a point in the right direction. I picked up "Learning jQuery" by Chaffer and Swedberg; hopefully that'll bring me closer to figuring how to do it. In the meantime, I suppose I'll stick with cfflush for my page; maybe I can scrap that and implement the solution you describe in a future release when I've gotten my feet a lot wetter with jQuery.

    For anyone else reading this who's had the same problem -- after looking at the example of cfflush in the CF8 Livedocs, I did a view source of the rendered page and realized the example renders neatly, 1 line at a time, only because it creates a massive amount of whitespace with each line. Other articles I've read explain that recent browers tend not to flush if there's too little data, and suggest using repeatString(" ", 250) with each flush to create enough data to convince IE to flush. With IE8 I've found that it has to be 100,000 instead of 250, in order to get my lines to come out neatly 1 at a time (that's with the cfflush interval set to a low value of 10, as in the Livedocs example). Not the most elegant solution, but in my case it's only an action page/popup; whitespace is less of an issue than it would be on a normal UI display page.
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2012
    Posts
    43
    Rep Power
    4

    Found a simple solution, but...


    I created a simple AJAX model of what I'm trying to do and 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?) Below is the code for the 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.
  8. #5
  9. No Profile Picture
    Moderator

    Join Date
    Jun 2002
    Location
    Raleigh, NC
    Posts
    5,281
    Rep Power
    968
    I would look at something like jQuery to do the Ajax calls, since it handles all the various browser inconsistencies.
  10. #6
  11. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2012
    Posts
    43
    Rep Power
    4
    Yeah, I would, but they don't have jQuery functionality on our servers yet (for the app I'm working on), unfortunately.

    A couple of small things I discovered, though:

    1) Since I'm getting synchronously (3rd argument in open() 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 has so much whitespace that the alert box will stretch down below the window and the text will be out of view. If I trim the response text, the alert box shows it just fine.
  12. #7
  13. No Profile Picture
    Moderator

    Join Date
    Jun 2002
    Location
    Raleigh, NC
    Posts
    5,281
    Rep Power
    968
    jQuery is just a JavaScript library, so if you can put files on the server you can use jQuery.

    To get rid of the whitespace, you can wrap everything before your cfoutput in cfsilent tags.
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2012
    Posts
    43
    Rep Power
    4
    You're preaching to choir . I've said the same thing to mgmt, but they're not ready to put the jQuery libraries on the servers just yet.

    As to the whitespace, it turns out the code I was using for the process was based on an example of cfflush from the CF8 Livedocs. I realized that that loop was intentionally creating whitespace in order to get the cfflush example to work. So, I got rid of the loop, and also added <cfsetting enablecfoutputonly="yes">, and also wrote a simple js trim function to clean up the response text clientside if cf doesn't do the trick. If you're interested, I've included my cleaner, simpler code below (I'm putting it in and continuing this thread only because I'm dead set on figuring out the problem for the sake of anyone else who encounters the same and goes web surfing for a solution).

    callAjax_v3.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 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>
    I'm also researching for an answer as to why the ajax has to be synchronous in this example in order for it to work. If I set the 3rd argument of the open() function to "true" rather than "false", and thus make it asynchronous, it doesn't work in either FF or IE8. It displays the first line only, and then it quits.
  16. #9
  17. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2012
    Posts
    43
    Rep Power
    4

    The answer...


    I finally found this blog post, which explains why I (and lots of other people) are having this problem with IE when using AJAX synchronously: http://www.blacksquare.co.za/blog/?p=458.

    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.

    If interested, I've been discussing this problem and my new approach in greater detail at http://www.codingforums.com/showthre...10#post1300010

IMN logo majestic logo threadwatch logo seochat tools logo