This weekend I exchanged a few emails with a reader about how you can handle very slow processes in a Model-Glue application. Typically folks will handle slow processes using one of these methods:
- cfflush: Print out a 'Please Wait' type message, use cfflush to flush out the content, and then start the slow process
- cfthread: You can use cfthread to either run a bunch of parallel slow processes at once, or 'fire and forget' a slow process
- scheduler: Use the ColdFusion scheduler to run the slow process completely outside the view of the site visitor.
Of course the best way to handle a slow process is to ensure you've done everything possible to speed it up. As an example, I was convinced that a particular process on coldfusionbloggers.org was slow because it had to be. Turned out it was a stupid SQL mistake on my part. So before any attempt is made to mitigate or hide a slow process, you need to do everything possible to ensure you haven't missed something obvious.
Once you've done that, what next? If you ever tried to use cfflush within a Model-Glue view, you know what happens:Because your view file ends up being run as a custom tag (behind the scenes) you can't use the cfflush tag. So what about cfthread? I created a simple demo application (available as a zip to this blog entry) using Model-Glue 3. I began by creating a new event, page.slow, that would represent my slow process:Message Unable to perform cfflush.
Detail You have called cfflush in an invalid location, such as inside a cfquery or cfthread or between a CFML custom tag start and end tag.
2 <broadcasts>
3 <message name="doItSlow" />
4 </broadcasts>
5 <results>
6 <result do="template.main" />
7 </results>
8 <views>
9 <include name="body" template="pages/slow.cfm" />
10 </views>
11 </event-handler>
2 <cfargument name="event" type="any" required="true">
3
4 <!--- First, am I running the slow process? --->
5 <cfif structKeyExists(application, "slowprocess")>
6 <cfset arguments.event.setValue("status", "ongoing")>
7 <cfset arguments.event.setValue("progress", application.slowprocess)>
8 <cfelse>
9 <cfset arguments.event.setValue("status", "began")>
10 <cfthread name="slowprocess" priority="low">
11 <cfset application.slowprocess = 0>
12 <!--- run 10 processes that take 1 minute each. --->
13 <cfloop index="x" from="1" to="10">
14 <cfset application.slowprocess++>
15 <cfset sleep(15000)>
16 </cfloop>
17 <cfset structDelete(application, "slowprocess")>
18 </cfthread>
19 </cfif>
20
21 </cffunction>
2 <cfset progress = event.getValue("progress")>
3 <cfset event.setValue("usemeta",true)>
4
5 <cfoutput>
6 <b>status=</b>#status#<br/>
7 <b>progress=</b>#progress#
8 </cfoutput>
2
3 <html>
4
5 <head>
6 <link rel="stylesheet" type="text/css" href="css/stylesheet.css"></link>
7 <cfif isBoolean(usemeta) and usemeta>
8 <meta http-equiv="refresh" content="10">
9 </cfif>
10 </head>
11
12 <body>
13 <div id="banner">Demo</div>
14
15 <!--- Display the view named "body" --->
16 <cfoutput>#viewCollection.getView("body")#</cfoutput>
17 </body>
18
19 </html>


Comment 1 written by Martijn van der Woud on 28 October 2008, at 3:26 AM
Comment 2 written by Joel Cox on 28 October 2008, at 9:31 AM
Comment 3 written by Raymond Camden on 28 October 2008, at 9:37 AM
Comment 4 written by Marf on 4 November 2008, at 3:31 PM
Comment 5 written by Don on 20 May 2009, at 2:16 PM
In your example, where would you put the "doitslow" function?
My thinking on this is that with fusebox using cfincludes, that I will have to include a template that starts the "LOADING" view and then includes the slow process itself. I'm leaning towards an iFrame tho.
Comment 6 written by Raymond Camden on 20 May 2009, at 3:15 PM
Comment 7 written by Don on 20 May 2009, at 3:30 PM
Comment 8 written by Raymond Camden on 20 May 2009, at 3:32 PM
[Add Comment] [Subscribe to Comments]