Async, Futures and Promises

118 views
Skip to first unread message

Jon Clausen

unread,
Mar 10, 2015, 2:56:29 PM3/10/15
to lu...@googlegroups.com
Since I brought it up, Andrew challenged me to start a separate thread for this, here it is. :)  

I’ve spent the last month in JS-land and, I have to say, I hope that any future for Lucee prioritizes non-blocking development with whatever product they evolve in to.    Even so, Javascript callbacks suck - though they have made me write better code in some ways. My biggest beef with server-side javascript has been scopes and callbacks, which force you to work effectively work backward through the request and make debugging and testing a nightmare.   Even promise-based development in JS requires you to jump through callback hoops.   

With that in mind, with distributed data, auto-scaling, API’s abounding, and the like, I’d love to see something beyond cfthread incorporated in to the core.   

Being able to develop sequentially, blocking when needed, and yet still open up non-blocking sections that can sprint on their own through the process of gathering data would be ideal.

(Very basic) Pseudo code example for a concept:

public void function process_stuff(event,prc){
    //Anything up to a closing semicolon is executed asynchronously
    async{
        var stuff = getModel('Stuff').findAll();
        //get other stuff
        promise(
            //scope or null
            prc,   
            //variable name, either scope or, if scope is null, it becomes a local variable
            'moreStuff',    
            //async event to ensure
            getModel('OtherStuff').findAll()
        );
          
    }
    //await example - would begin blocking again until the next action is completed
    await(
        prc.moreStuff,
        function(){
            //do stuff with moreStuff
        });
      
    //future() usage example, would block function execution until the promise arguments are met      
    event.setValue(‘doubleStuff’,{future(stuff),future(prc.moreStuff)});

}


The idea with the future() method as a function argument would be to keep it non-blocking until the argument, itself, was called called explicitly.  So, if we didn’t want to deal with await, we could just have the future() serve as a pseudo-await() like so:

doStuffWithMoreStuff(future(prc.morestuff)) - which would, in effect, do the same thing as the await() block above.

I’m not a language development expert like many here, so this is a very uninformed poof-of-concept.  There would also have to be a server-size timeout mechanism to handle awaits and futures that never materialize - as you could lock up an entire request, and possibly a server, with an incomplete callback or unfulfilled promise.  

Anyway, I wanted to open the topic up to discussion, as I think it’s an important one for the future of Lucee.

Jon

Andrew Dixon

unread,
Mar 10, 2015, 3:27:32 PM3/10/15
to lu...@googlegroups.com
Lets be clear it was Adam Cameron not me or someone else called Andrew who suggested a separate thread :-) 

I'm not really that familiar with the topic, so can you explain how this differs from using cfthread with either the "join" or "run" actions specified, as from the above it would appear to me to be the same thing.

Kind regards,

Andrew
about.me
mso - Lucee - Member

--
You received this message because you are subscribed to the Google Groups "Lucee" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lucee+un...@googlegroups.com.
To post to this group, send email to lu...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/lucee/etPan.54ff3e57.54fc792c.f65b%40jonclausen-mbp.local.
For more options, visit https://groups.google.com/d/optout.

Jon Clausen

unread,
Mar 10, 2015, 4:26:35 PM3/10/15
to lu...@googlegroups.com

Sure. Sorry, meant “Adam”, typed “Andrew”… Oops.

The fundamental issue with threads is that they require an explicit join at some point. A future() or await() would accomplish somewhat the same thing, but the join mechanism would would be blocking on the explicit call for the promised variable. Plus, CFThread is, IMHO unnecessarily verbose for what it accomplishes. Let’s take my pseudo code in cfthread:

public void function process_stuff(event,prc){
    //I have to implicitly create the thread, and have to scope it afterward
    thread action="run" name="stuff"{
        thread.returnValue=getModel('Stuff').findAll();
    }

    //Ditto
    thread action="run" name="otherstuff"{
        thread.returnValue=getModel('OtherStuff').findAll();
    }

    // here's where I wait for them all to come together again,
    // everything is blocked from here on out  
    // plus I have to be very verbose and re-scope
    thread action="join" thread="stuff,otherstuff";

    now I re-scope the variables *sigh*
    var stuff = cfthread.stuff;
    prc.otherstuff = cfthread.otherstuff;

    //do more stuff with stuff *sigh*
    for(thing in moreStuff){
        //do things to stuff
    }

    event.setValue('doubleStuff',{stuff,prc.moreStuff})

}

If you go back to my original example, blocking for “stuff” doesn’t happen until the variable is explicitly called in event.setValue(), while blocking does occur for prc.moreStuff in the await tag, I could also make promises to be passed as futures to the other arguments or even pass futures in to scopes.

Let’s take this one step further. The server has control of the request (as in cfflush). Let’s say we were to add a function,  ( e.g. - toDocumentObject(varName,future,[exclude]) ) which would automatically deliver a JSON object before the document was closed. Rendering could begin and our request would appear to have very low latency, but something like AngularJS could pick up the object and use it tor render data once it has been added to the document.  For something like data from a web service, this could really speed things up, without slowing things down waiting for the third-party request to process.

toDocumentObject(myStuff,future(stuff),[{"private":true}]);

This would allow the developer to block for necessary data but use a single request, rather than a separate AJAX call to pull the JS variables needed to render the non-essentials. 

CFThread, IMHO, is cumbersome and requires to begin blocking far to early.

Jon

Jesse Shaffer

unread,
Mar 10, 2015, 5:37:34 PM3/10/15
to lu...@googlegroups.com
Maybe I'm just not getting it, but the entire body of process_stuff blocks until the "async" code is completed?  What is the benefit of using promises here?  Would you get a promise for an async block without blocking on its return?  Maybe I'm missing something, but it's shorter syntax for doing a cfthread and joining it, which means no added benefit for me.

I would like the idea better of JS-style promises better:

process_stuff(event) {
   
// this is more similar to the way JS promises appear to work.
   
// the benefit is, you do not have to block process_stuff from returning - it is truely async.
   
var promise = runAsync(function() {
      return getModel("Stuff").findAll();
   
}) // promise returned from runAsync
     
// if the async function is complete at the time the handler is attached, the handler is immediately run. otherwise it is run as a callback on the async function's completion.
     
.onSuccess(function(returnValue) {
         
event.setValue("stuff", returnValue);
         // then maybe:
         
// event.continue();
     
})
     
.onFailure(function() {
         
// handle exception
     
})
     
.onCompletion(function() {
         
// do something whether it is successful or failed
     
});
   
   
// or more fluently (although it doesnt look right if you run something async and do not attach any handlers...)
   
var promise = when(function() {}) // when() returns a promise
     
.succeeds(function(){})
     
.fails(function() {})
     
.completes(function() {})
     
;
   

   
// then, if you absolutely must block until the promise is done processing:
   promise
.block();
}

Jesse Shaffer

unread,
Mar 10, 2015, 5:41:38 PM3/10/15
to lu...@googlegroups.com
Would you get a promise for an async block without blocking on its return?

Rather, "Could you". 

Jon Clausen

unread,
Mar 10, 2015, 5:52:23 PM3/10/15
to lu...@googlegroups.com
Yes, my pseudo code doesn’t illustrate the benefit beyond a few examples, and you’re right the function body is blocked in the end.  I don’t mind the JS style promises, either (other than the callback nesting) and I like the .on[Result] methods for error handling.

What I do like about the example I proposed was that the promise can scope the variable in to a referenced object in one call and that key can reference the future fulfillment of the promise - in or outside of the function body.  It basically creates a separate type object that exists as that type until it is fulfilled, when it turns in to the assigned type.

The JS style promise in your example doesn’t incorporate the future() call, which can be in or outside of the function body.    This makes handling the variable tricky and probably gives nightmares to purists, but it gives flexibility in request assembly.  You could even include a default argument in the future, if the promise isn’t fulfilled and omit the “onFailure” methods to let let it fail gracefully, without all of the conditionals (let logging handle it?)

future(myVar,”The requested information could not be retrieved”).



Jon

Adam Cameron

unread,
Mar 11, 2015, 4:24:50 AM3/11/15
to lu...@googlegroups.com


On Tuesday, 10 March 2015 18:56:29 UTC, Jon Clausen wrote:
Since I brought it up, Andrew challenged me to start a separate thread for this, here it is. :)  

Cheers, Jim, for starting this thread :-p

If ppl are confused or unclear about how futures / promises etc work, and why one might want to use them, and how they differ from just using thread blocks, perhaps some background reading might help:

Implementations:

(I list these because some language implementations/docs might make things more clear than others, and there are idiosyncrasies with both).

In production I've used them with PHP via Guzzle, and kinda document their usage in a coupla blog articles:

Note that these are more focusing on Guzzle itself, but this uses futures for making async HTTP calls, so there's "real world" usage in there.

I'm sure other ppl have got some real-world examples they could share too? Examples always make understanding easier, IMO. I also think knocking together a CFML implementation of an approach to this should be fairly easy, and something I've thought about doing, so if I find time amongst my PHP work & other bits 'n' pieces, I might write some code.

I suggest this RTFM because it's all pretty well-trod ground outside of CFML, so it might be good preparation for participating in this conversation.  I - for one - still think I have some reading to do before I start piping up further, forming opinions, or asking questions.

I will say, though, that I think this is a really good idea.

-- 
Adam
Reply all
Reply to author
Forward
0 new messages