In ColdFusion, I am a large fan of utilizing Closures to create a clear separation of issues between the enterprise logic and the low-level mechanics required to execute a given algorithm. I’ve used closures for issues like managing temp directories, pulling sources out of a connection pool, and implementing distributed locks. And, in relation to executing CFThread
tags, I nearly all the time break up my asynchronous code from my enterprise logic. Nevertheless, it wasn’t till the opposite day that it occurred to me that I may in all probability use Closures to simplify the execution of asynchronous CFThread
tags in ColdFusion.
The fundamental thought behind utilizing a ColdFusion Closure to create a separation of issues is that you just go the closure out of scope and into one other operate. This different operate then handles the low-level, non-business-logic code and invokes the passed-in closure as a part of the execution. The sample seems one thing like this (pseudo-code):
<cfscript>
runMyClosure(
() => {
// The enterprise logic right here inside my closure.
}
);
// ---
// The low-level mechanics are dealt with right here on this different methodology.
// ---
non-public void operate runMyClosure( required operate operator ) {
// Low-level mechanics right here.
// Low-level mechanics right here.
operator(); // Execute the passed-in operator.
// Low-level mechanics right here.
// Low-level mechanics right here.
}
</cfscript>
Usually, when utilizing this system, I am executing synchronous code, like file I/O; and, the closure is admittedly simply there to separate the logic from the mechanics. A decade in the past, when ColdFusion 10 got here out, I did a whole lot of experimenting with passing closures right into a CFThread
tag; nevertheless, till now, it by no means occurred to me that I’d use closures to utterly encapsulate the CFThraed
tag logic itself.
To discover this concept, I’ve created a mock ColdFusion element for creating customers. It has one public methodology, createUser()
, that should carry out some asynchronous logging as a part of its workflow. This logging goes to be outlined inside a ColdFusion closure which will probably be invoked inside an asynchronous CFThread
tag. Nevertheless, the CFThread
mechanics will probably be encapsulated inside their very own methodology, runSafelyInThread()
.
element
output = false
trace = "I present service strategies for customers."
{
/**
* I create a person document with the given credentials.
*/
public numeric operate createUser(
required string e mail,
required string password
) {
var person = {
id: randRange( 1, 999999 ),
e mail: e mail,
password: password,
createdAt: now()
};
// The given closure will probably be executed inside a CFThread physique. Any errors that
// happen in the course of the spawning / execution will probably be safely caught and logged.
runSafelyInThread(
() => {
cfdump( var = "Consumer [#user.id#] created.", output = "console" );
}
);
return( person.id );
}
// ---
// PRIVATE METHODS.
// ---
/**
* I encapsulate the asynchronous spawning and execution of the given operator.
*
* In terms of operating code asynchronously inside a CFThread tag, there are two
* issues that may go flawed: first, the JVM is likely to be out of threads (very uncommon) and
* cannot really spawn a brand new thread so that you can run. And second, as soon as the thread has
* been spawned and is operating, the code executing contained in the CFThread physique would possibly throw
* an error that's misplaced into the ether. To encapsulate the error dealing with, all of the
* enterprise logic will probably be contained contained in the passed-in Operator; and, the entire low-
* stage mechanics of safely operating a thread will probably be dealt with inside this methodology.
* --
* NOTE: There may be additionally the potential of "sudden thread dying"; however, I do not consider
* we will deal with that kind of error right here.
*/
non-public void operate runSafelyInThread( required operate operator ) {
attempt {
thread
title = "UserService.runSafelyInThread.#createUuid()#"
motion = "run"
operator = operator
{
attempt {
operator();
} catch ( any error ) {
logError( error, "Operator error inside runSafelyInThread operator." );
}
}
// There is a tiny likelihood that the JVM is out of threads as a consequence of excessive load. Catch
// these errors in order that the dad or mum request does not blow up.
} catch ( any error ) {
logError( error, "CFThread couldn't be spawned in runSafelyInThread." );
}
}
/**
* I log the given error and message to the console.
*/
non-public void operate logError(
required any error,
required string message
) {
cfdump( var = message, output = "console" );
cfdump( var = error, output = "console" );
}
}
As you may see, the runSafelyInThread()
methodology takes care of error dealing with, each inside and outdoors of the CFThread
tag. And, this methodology handles the execution of the passed-in ColdFusion closure as soon as the the asynchronous thread has been efficiently spawned. This enables the passed-in closure to worry-not in regards to the threading and focus solely on the logging.
Discover additionally that the closure is referencing the person
object with is a part of the native scope of the lexical context!
To do that out, I created a easy take a look at web page that creates a single person:
<cfscript>
id = new UserService()
.createUser( "skroob@spaceballs.film", "12345" )
;
writeOutput( "Woot woot, #id# created." );
</cfscript>
If we run this ColdFusion code – within the newest Adobe ColdFusion or Lucee CFML – and we take a look at the logs, we get the next output:
[INFO] string Consumer [990685] created.
That is the logging that was generated by our ColdFusion closure that was handed into the CFThread
tag.
Not solely did the ColdFusion closure preserve the bindings to its lexical scope after it was handed into the CFThread
tag, we have been capable of create a clear separation of issues between the enterprise logic (the logging) and the low-level mechanics (the thread spawning). It is sort of astonishing how simple ColdFusion makes it to run code asynchronously.
runSafelyInThread()
vs runAsync()
You would possibly take a look at my runSafelyInThread()
methodology and surprise how it’s any completely different from ColdFusion’s native, built-in operate, runAsync()
. The runAsync()
operate takes a closure and it returns a Future
, which is considerably akin to JavaScript’s Promise
object.
In concept, the runAsync()
performance is a superb idea. Nevertheless, up to now, after I’ve performed round with the runAsync()
operate, I’ve stumbled over many obstacles starting from when it blocks to the way it handles (or moderately does not deal with) errors. It’s extremely attainable that within the years since I’ve appeared on the preliminary implementation of runAsync()
, all of those bumps have been smoothed out. Nevertheless, in the interim, I discover the CFThread
tag a lot simpler to motive about.
Wish to use code from this put up?
Try the license.