Function Listeners

Function Listeners

Lucee 6.1 introduced a new feature called "Function Listeners". This allows you to execute a function (of any kind) in parallel, so you do not have to wait for the result of the execution, similar to using the cfthread tag. Function Listeners provide an easy syntax to not only execute a function in parallel but also include support to handle the result by simply adding a listener after the function call. This listener can handle the result or exception of a function.

Simple Version

This example demonstrates a simple Function Listener that executes mySuccess in parallel and sets a variable with the result.

function mySuccess() { return "Susi Sorglos"; }

mySuccess():function(result,error) { request.testFunctionListener=result; }; // wait for the thread to finish sleep(100); dump(request.testFunctionListener ?: "undefined1");

Join thread thread

Instead of "run and forget" you can also join the thread with help of the function threadJoin (or the tags <cfthread action="join">). You get a name from the call and that name you can use to join it. The thread information is available in the scope cfhread like a regular thread.

function mySuccess() { return "Susi Sorglos"; } // the function call returns the name of the thread threadName=mySuccess():function(result,error) { thread.result=result; }; // you can then use the name to join the thread threadJoin(threadName);

// and for example see the result in cfthread dump(cfthread[threadName].result);

Handling Exceptions

In this case we see what happens when the function throws an exception. in that case the argument error is provided to the listener function, you can then for example send it to the log.

// function that throws an exception function myError() { throw "Upsi dupsi!"; }

// storing the exception message in the thread scope threadName=myError():function(result,error) { thread.result=error.message; };

// wait for the thread to finish threadJoin(threadName);

// see the result dump(cfthread[threadName].result);

Listening on a Built-in Function (BIF)

Instead of user defined functions, you can also listen to build in functions as well.

threadName=arrayLen([1,2,3]):function(result,error) { thread.result=result; }; // wait for the thread to finish threadJoin(threadName);

dump(cfthread[threadName].result);

Listening on a variable "Chain"

You can also listen to variable "chain".

function mySuccess() { return "Susi Sorglos"; } // create a chain a.b.c.d=mySuccess; threadName=a.b.c.d():function(result,error) { thread.result=result ?: error; }; // wait for the thread to finish threadJoin(threadName);

dump(cfthread[threadName].result);

Listening on a Component Instantiation

You can also listen to a component instantiation.

threadName=new Query():function(result,error) { thread.result=result; };

// wait for the thread to finish threadJoin(threadName);

dump(getMetadata(cfthread[threadName].result).fullname);

Listening on a Static Component Function

You can also listen to a static component function.

threadName=Query::new(["columnName"]):function(result,error) { thread.result=result; };

// wait for the thread to finish threadJoin(threadName);

dump(cfthread[threadName].result.columnlist);

Function Collection Listener

A listener does not need to be a function, it also can be a function collection (multiple functions inside a struct). This way you can define a function for specific events like onSuccess or onFail like this.

function mySuccess() { return "Susi Sorglos"; }

threadName1=mySuccess():{ onSuccess:function(result) { thread.success=result; } ,onFail:function(error) { thread.fail=error.message; } };

// wait for the thread to finish threadJoin(threadName1); dump(cfthread[threadName1].success);

Component Listener

You can also define a component instance as a listener, in this case we do a inline component.

function mySuccess() { return "Susi Sorglos"; }

threadName1=mySuccess():new component { function onSuccess(result) { thread.success=result; } };

// wait for the thread to finish threadJoin(threadName1); dump(cfthread[threadName1].success);

No Listener

In case you wanna simply a asynchron execution, but you don't care about the outcome, you can define no listener at all, simply pass null (in this case i use the function nullValue(), because only with full null support enabled, the constant null is available).

// writing some data to the request scope function logAndFail(name,value) { request.testFunctionListenerEcho[name]=value; throw "Upsi dupsi!"; }

threadName1=logAndFail("testNull","Peter Lustig"):nullValue();

// wait for the thread to finish threadJoin(threadName1);

// reading the data stored to the request scope dump(request.testFunctionListenerEcho.testNull);

Instead of null, you can also simply pass a empty struct or a component not defining the function needed.

// function collection with no listeners
threadName2=logAndFail("testStruct","Ruedi Zraggen"):{};

// function collection with no listeners threadName2=logAndFail("testStruct","Ruedi Zraggen"):new component {};

See also