As yet another followup to my blog entry on CFTHREAD, a user asked about how to present a 'Please stand by' type message while the threads were running. This is fairly trivial with JavaScript and CFFLUSH:
<cfset threadlist = "">
<cfloop index="x" from="1" to="5">
<cfset name = "find more cowbell #x#">
<cfset threadlist = listAppend(threadlist, name)>
<cfthread name="#name#">
<cfset sleep(3000)>
<cflog file="tdemo" text="All done with #thread.name#, baby.">
</cfthread>
</cfloop>
<cfoutput>
<span id="loader">
#repeatString(" ",250)#
Please stand by...
</span>
</cfoutput>
<cfflush>
<cfthread action="join" name="#threadlist#" />
<script>
document.getElementById('loader').innerHTML = ''
</script>
<cfdump var="#cfthread#">
This is a slightly modified version of my previous code entry. Notice that I've added a span called loader with a bit of HTML. (The white space in front is to ensure IE renders the text.) After the cfthread/join action, I then use a bit more JavaScript to get rid of the loader. That's it. I'd normally use jQuery and some fancy loading graphic (like a unicorn, a magical unicorn), but hopefully you get the idea.
Archived Comments
I need to dig around and find out how to do a 'spinning pinwheel' type of waiting thing....
Download the image form here: http://ajaxload.info/ and replace my text with an IMG tag. That's it.
"trivial"????????
Hmmm. Now I have to find out what CFFLUSH does. You know it is amazing that I've worked with CF since version 4. Done amazing things with it. And I still keep finding things I have no clue about. I wonder if it is because I'm always under deadlines and rarely have time just to play with it? I find this stuff when I have a problem. Good thing there are people who have that time whose brains I can tap.
Normally CF will send the page back when EVERYTHING is done. cfflush is a way to say "Send what you have so far." It doesn't always work (see the docs), but there ya go. :)
This was a nice idea, but the cfflush tag doesn't work inside frameworks. I'm working on a system that was built on Fusebox (old version) so everything is cfincluded. Maybe I can go to the very top tho. hmmmmmmm Smoke will be pouring from my ears soon.
http://www.coldfusionjedi.c...
I'm not knocking Ray and I know that he's just trying to help someone out but do you know what I would do? I would complicate this and do it with a combination of cflayoutarea's, cfdiv's and the almighty ColdFusion.navigate - partly because I'm a navigate kind of guy but mostly because I could do it without resorting to using cfflush at all. Why complicate it you may ask and why pick on tags that have been around since before I started using CF? Well mostly because of this...
* Using any of the following tags or functions on a page anywhere after the cfflush tag can cause errors or unexpected results: cfcontent, cfcookie, cfform, cfheader, cfhtmlhead, cflocation, and SetLocale. Similarly, do not use any tags that use AJAX features, including cfdiv, cflayout, cflayoutarea, cfpod, cfsprydataset, cftooltip, cfwindow, or HTML format cfgrid, cftree, cftextarea, or cfinput (using autosuggest or datefield attributes) tags. All of the preceding tags and functions normally modify the HTML header, but cannot do so after a cfflush tag, because the cfflush sends the header.
* Using the cfset tag to set a cookie anywhere on a page that has a cfflush tag does not set the cookie in the browser.
* Using the cfflush tag in the body of several tags, including cfsavecontent, cfquery, and custom tags, causes errors.
* If you save Client variables as cookies, any client variables that you set after a cfflush tag are not saved in the browser.
* Using any of the following tags or functions on a page anywhere after the cfflush tag can cause errors or unexpected results: cfcontent, cfcookie, cfform, cfheader, cfhtmlhead, cflocation, and SetLocale. Similarly, do not use any tags that use AJAX features, including cfdiv, cflayout, cflayoutarea, cfpod, cfsprydataset, cftooltip, cfwindow, or HTML format cfgrid, cftree, cftextarea, or cfinput (using autosuggest or datefield attributes) tags. All of the preceding tags and functions normally modify the HTML header, but cannot do so after a cfflush tag, because the cfflush sends the header.
* Using the cfset tag to set a cookie anywhere on a page that has a cfflush tag does not set the cookie in the browser.
* Using the cfflush tag in the body of several tags, including cfsavecontent, cfquery, and custom tags, causes errors.
* If you save Client variables as cookies, any client variables that you set after a cfflush tag are not saved in the browser.
Andy, Ajax would indeed be a good alternative. Only issue is that you would need to ping every N seconds to get the status.
Well then it's a good thing that I figured out how to do that as efficiently as possible last week!
http://www.coldfusionjedi.c...
Is it possible to tell the status of a free running thread? I was thinking (very dangerous) that I could set the query to run in a thread and go on to other things and when it is ready, use it.
Or can it store the data and have it ready when I am?
Maybe I should just go test this. Like can I create a session variable that holds the query and then periodically check and see if that variable exists and when it does, use it?
You can get the status of a thread while still on the same page. After that, the only way to determine what's going on in a thread is for the thread to broadcast stuff out. It can do this by just setting an application variable. That variable can be checked by other requests.
Hi Ray, thanks for posting this! I'm just now getting my head wrapped around threads in CF. However, using your example on a script that I created, I'm having a bit of trouble and maybe you can shed some light on it.
I have a page which submits form data to a CF page that does quite a bit of processing before it loads the HTML portion of the page. Using your example, everything seems to work as expected with one issue. A variable that I create within the first thread doesn't seem to be available in the page thread (just after I join them). Even if I prefix the variable with the thread name, I still get an error: "Element X is undefined in THREAD". The way I read the CF8 documentation, it says that the page thread can access other threads on the page by using the prefix...am I reading this incorrectly? Thanks!
Can you post your code to pastebin and post the url here?
Hi Ray,
I think I actually figured it out after some trial and error. I was prefixing the variable with my thread name. When I changed it to THREAD.variable and then accessed it in the page thread as THREAD.variable it worked! For some reason, myThreadName.variable didn't seem to work...but it could have been that my head was so deep in the code that I wasn't referencing it correctly down the page. Anyhow, thanks for the super quick response, and thanks for this post..it did exactly what I needed!
Hi Ray,
sorry for digging that deep in time ...
Just checked your code here and it does not seems to work for me (FFox 3.612 / CF 8,0,1,195765).
I highly suspect <cfflush> ... but not certain of this.
Is there any other way to display main page message (or animated image) while thread(s) is (are) executing ?
For what I've tried now, we have to wait until threads have finished to display the page, wether they are named or not.
Sorry again for upping that *old* subject,
best regards,
A.
Well I crunched a lot my head ...
And finally, I may suggest one thing, based on binded cfc.
I'm not sure I'm not going nowhere but in the wrong direction, anyway I couldn't leave my screen as is.
First, I create a quite simple cfc :
[code]
<cfcomponent>
<cffunction name="sendmail" access="remote" output="false">
<cfargument name="numberOfIt" displayname="nb" hint="nb of iterations we call in the bind" type="numeric" default="10" required="no" />
<cfargument name="Speed" displayname="speed" hint="speed interval in millisecs we call in the bind" type="numeric" default="20" required="no" />
<cfloop from="1" to="#numberOfIt#" index="nbbs">
<cflog file="ABdemo" text="This is mail #nbbs# #timeformat(now(),"long")#">
<cfscript>
sleep(Speed);
</cfscript>
</cfloop>
<cfreturn "It's over now !"/>
</cffunction>
</cfcomponent>
[/code]
The tip is both here and in the form page; in fact we will only update the form field value ... providing start (default value of the field) and end (cfc returned value) texts.
Now, I set a very simple <cfform> page :
<head>
</head>
<body>
<cfform>
<cfinput type="text" value="Starting, please wait ..." bind="cfc:threadedMail.sendmail(5,100)" bindonload="yes" name="CFCstatus" id="CFCstatus">
<!--- The hint is here : default value will display first before CFC's finishes to run the code --->
</cfform>
</body>
</html>
Et voilĂ ... but just for start and end. Because - as much of you know (I didn't) - firing a <cfresult> is causing the function to stop, so that only first iteration of the loop would be ran.
Many questions still remain ...
1/ Is this method "load safe", or even "clean"
2/ Should I prefer to use <cfthread action="sleep" duration="Speed"> instead of CFScript sleep(Speed) ?
3/ Based on bind capabilities, I'll be surprised if none of you could find a workaround to update the CFCStatus field while in the loop
4/ And finaly, as my final goal is to use this to slow down CFmail campaigns, by grouping and slow down the send process ... how do you think this will fit my needs ?
Sorry for my froggy English,
Thanks in advance for any suggestion.
Antoine.
To your first reply - it could be a few things. Browsers decide when to render content. IE, for example, is picky about when it will show stuff. So it could have just been FF saying, "I know you sent me some HTML, but I just don't feel like showing it yet."
To your second reply: First off, I think doing it via Ajax is _better_. My code above is cool for simple stuff, but handing it off to an Ajax request gives you more control. Therefore - it's safe in my regards. Is it "load safe" - well that depends. If I reload your page many times it will keep making the Ajax request many times. You want to ensure your process _knows_ that it started and doesn't start off again. The code you have above will not do that.
You almost NEVER want to sleep. I used sleep just for demonstrative purposes. It was a way to make things slow on purpose. There are real needs for it, but generally, you should not be using it.
3) I'd just switch to using a jQuery solution. :)
4) oh! So then you may indeed want to use sleep. :) So yeah, I can see that making sense for you.
@Ray : Thank you so much.
Should I post here modified code (I mean with some Thread control script) ?
Sure - or post a pastebin URL.