Async Programming
ColdBox Promises, Executors, Async programming and Parallel Computations
Last updated
ColdBox Promises, Executors, Async programming and Parallel Computations
Last updated
ColdBox 6 introduces the concept of asynchronous and parallel programming using Futures and Executors for ColdFusion (CFML). We leverage the entire arsenal in the JDK to bring you a wide array of features for your applications. From the ability to create asynchronous pipelines, to parallel work loads, work queues, and scheduled tasks.
YOU DON'T NEED COLDBOX TO RUN ANY SCHEDULED TASKS OR ANY FEATURES OF THE ASYNC PACKAGE. YOU CAN USE ANY OF THE STANDALONE LIBRARIES BY USING CACHEBOX, WIREBOX OR LOGBOX STANDALONE.
Our async package coldbox.system.async
is also available for all the standalone libraries: WireBox, CacheBox, and LogBox. This means that you can use the async capabilities in ANY ColdFusion (CFML) application, not only ColdBox HMVC applications.
We leverage Java Executors
, CompletableFutures
and much more classes from the concurrent packages in the JDK: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/package-summary.html
We have created a full sample gallery that we use in our live sessions and trainings. It contains tons of samples you can run and learn from: https://github.com/lmajano/to-the-future-with-cbFutures
We have created a manager for leveraging all the async/parallel capabilities. We lovingly call it the ColdBox AsyncManager
. From this manager you will be able to create async pipelines, simple futures, executors and much more.
A ColdBox future is used for async/parallel programming where you can register a task or multiple tasks that will execute in a non-blocking approach and trigger dependent computations which could also be asynchronous. This Future object can then be used to monitor the execution of the task and create rich completion/combining pipelines upon the results of such tasks. You can still use a get()
blocking operation, but that is an over simplistic approach to async programming because you are ultimately blocking to get the result.
ColdBox futures are backed by Java's CompletableFuture
API, so the majority of things will apply as well; even Java developers will feel at home. It will allow you to create rich pipelines for creating multiple Futures, chaining, composing and combining results.
You might be asking yourself, why should I leverage ColdBox futures instead of traditional cfthreads
or even the CFML engine's runAsync()
. Let's start with the first issue, using ColdBox futures instead of cfthread
.
cfthread
vs ColdBox Futurescfthreads
are an oversimplification approach to async computations. It allows you to spawn a thread backed by a Java Runnable and then either wait or not for it to complete. You then must use the thread
scope or other scopes to move data around, share data, and well it can get out of hand very quickly. Here are some issues:
Too over-simplistic
Threads limited on creation
Cannot be completed manually
No concept of a completion stage pipeline
No control of what executor runs the task
No way to trap the exceptions and recover
No way to do parallel computations with futures
No way to get a result from the computation, except by using a shared scope
You must track, name and pull information from the threads
etc.
You get the picture. They exist, but they are not easy to deal with and the API for managing them is poor.
runAsync()
vs ColdBox FuturesColdFusion 2018 and Lucee 5 both have introduced the concept of async programming via their runAsync()
function. Lucee also has the concept of executing collections in parallel via the each(), map(), filter()
operations as well. However, there is much to be desired in their implementations. Here are a list of deficiencies of their current implementations:
Backed by a custom wrapper to java.util.concurrent.Future
and not Completable Futures
Simplistic error handler with no way to recover or continue executing pipelines after an exception
No way to choose or reuse the executor to run the initial task in
No way to choose or reuse the executor to run the sub-sequent then()
operations. Lucee actually creates a new singleThreadExecutor()
for EVERY then()
operation.
No way to operate on multiple futures at once
No way to have one future win against multiple future operations
No way to combine futures
No way to compose futures
No ability to schedule tasks
No ability to run period tasks
No ability to delay the execution of tasks
Only works with closures, does not work on actually calling component methods
And so much more
All of our futures execute in the server's common ForkJoin
pool the JDK provides. However, the JDK since version 8 provides you a framework for simplifying the execution of asynchronous tasks. It can automatically provide you with a pool of threads and a simple API for assigning tasks or work loads to them. We have bridged the gap between Java and ColdFusion and now allow you to leverage all the functionality of the framework in your applications. You can create many types of executors and customized thread pools, so your work loads can use them.
Some resources:
The manager will be registered in WireBox as AsyncManager@ColdBox
or can be retrieved from the ColdBox main controller: controller.getAsyncManager()
.
The super type has a new async()
method that returns to you the instance of the AsyncManager
so you can execute async/parallel operations as well.