The basic idea is to encapsulate long-running code, such as a query, in a thread; when the thread completes, the query is saved to the session scope; meanwhile, the calling page uses a meta-refresh to periodically check whether the query has finished. If the query is still running, a "Loading..." message is displayed; if the query has finished, then you can do whatever you like to display the results.
First off, let's create some variables to help keep track of whether the thread has started and whether the query has completed. We'll also create a URL variable to help refresh the session variables for the purpose of testing.
<!--- Initialize parameters ---> <cfparam name="SESSION.threadStarted" default="0"> <!--- Track whether the thread is running ---> <cfparam name="SESSION.qRecords" default=""> <!--- Track whether the query has finished ---> <cfparam name="URL.refresh" default="0"> <!--- A control to refresh the session variables --->
Next, we'll start a threaded query if either the thread hasn't started yet or if the refresh variable is passed in the URL:
<!--- If a thread hasn't been started yet or the page should be refreshed ---> <cfif not SESSION.threadStarted or Val(URL.refresh)> <cfset SESSION.qRecords = ''> <cfset SESSION.threadStarted = 1> <!--- Create a threaded query ---> <cfthread action="run" name="tRecords"> <cfquery name="SESSION.qRecords" datasource="someDSN"> SELECT * FROM someTable </cfquery> </cfthread> </cfif>
Now we just write an HTML document with a couple of conditional statements in it. If the query is still running, the page will contain a meta refresh and a loading message. But, if the query has finished, the page shows the results of the query (or whatever else you would need to do).
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Loading records</title> <cfif not IsQuery(SESSION.qRecords)><meta http-equiv="refresh" content="3;url=thread-test.cfm"></cfif> </head> <body> <div align="center"> <!--- If the query has finished running ---> <cfif IsQuery(SESSION.qRecords)> The thread has completed. The query contains <cfoutput>#SESSION.qRecords.recordCount#</cfoutput> records.<br /> <a href="thread-test.cfm?refresh=1">Rerun the page.</a> <cfelse><img src="loading-gif.gif" width="16" height="16" alt="" /><br /> The query has not completed. </cfif> </div> </body> </html>
In total, the code should look like this:
<!--- Initialize parameters ---> <cfparam name="SESSION.threadStarted" default="0"> <!--- Track whether the thread is running ---> <cfparam name="SESSION.qRecords" default=""> <!--- Track whether the query has finished ---> <cfparam name="URL.refresh" default="0"> <!--- A control to refresh the session variables ---> <!--- If a thread hasn't been started yet or the page should be refreshed ---> <cfif not SESSION.threadStarted or Val(URL.refresh)> <cfset SESSION.qRecords = ''> <cfset SESSION.threadStarted = 1> <!--- Create a threaded query ---> <cfthread action="run" name="tRecords"> <cfquery name="SESSION.qRecords" datasource="someDSN"> SELECT * FROM someTable </cfquery> </cfthread> </cfif> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Loading records</title> <cfif not IsQuery(SESSION.qRecords)><meta http-equiv="refresh" content="3;url=thread-test.cfm"></cfif> </head> <body> <div align="center"> <!--- If the query has finished running ---> <cfif IsQuery(SESSION.qRecords)> The thread has completed. The query contains <cfoutput>#SESSION.qRecords.recordCount#</cfoutput> records.<br /> <a href="thread-test.cfm?refresh=1">Rerun the page.</a> <cfelse><img src="loading-gif.gif" width="16" height="16" alt="" /><br /> The query has not completed. </cfif> </div> </body> </html>
Note: I tried to access the thread directly across page refreshes (ie, across different page requests) by putting it in the session scope, but it didn't seem to work. If anyone can let me know of a way to access threads once the parent request ends, please let me know.

Comments (5)
June 1, 2007
16:36PM | #
Forgive me for not reading your entire post (hey it's almost 5pm on friday ;) - but from what I gather what you're trying to do could be greatly simplified by taking advantage of the new Ajax UI stuff. Specifically cflayout/cflayoutarea, cfwindow, cfpod, cfdiv - all of these will automatically display a 'loading' icon for you while the content as loading asynch. If I'm not mistaken the icon could even be customized with your own icon.
June 1, 2007
18:16PM | #
Tom:
I like your idea, but I am not sure it is that great of a use of coldfusion threads, when the same effect can be created using javascript and not tying up the server...
What I usually do on a long running page is I have a div displayed on screen that is absolutely positioned in the center of the screen with a "Please wait loading." message displayed...
Then as the loading of the page progresses (queries being run, huge tables being built, etc) I call a javascript function that updates a progress bar in the div to give the user a visual sense of the page progression.
When the page is done loading (right before outputing the tag) I call another javascript function to hide this "loading" div. The result is that the user gets the same experience that you would from the likes of Travelocity, but I don't actually need a page redirect or anything...
I realized that I don't explain things very well, and I don't have that much time to blog this myself right now, so I took some of my code and made a small demo...
small window loading message
full screen loading message
I tried using the new CFThread to sleep instead of the loops, but the page stopped working... i think that CFThread was stopping the CFFLush from working maybe?
Anyway, I will try to blog this over the weekend and include explainations of the code bits.
Oh, and a disclaimer... I tend to crawl the net and borrow heavily from other projects when I need a bit of javascript or css, I am sure most of this is not my original code...
June 2, 2007
02:37AM | #
@Todd: The use of AJAX is a very interesting idea, I have to admit. It certainly would work that way. I do think there's a need, though, for a non-JavaScript solution, since you can't always count on users having JavaScript and/or AJAX enabled in their browsers.
@Nick: The problem with queries is that you don't have any idea how long they're really going to take, and it's not possible to show a progress bar for them. And thanks for linking to the demos-- I can see how they work, but I didn't see the progress bar move. Can you update them?
October 28, 2007
07:01AM | #
Good idea, thx.
February 20, 2008
15:05PM | #
The links in Tom's comment are broken. Could someone please point to a working link?