Created
March 20, 2017 03:32
-
-
Save dajester2013/58f8304c0d3e3dd8b2037b60b4365652 to your computer and use it in GitHub Desktop.
Promises and Async
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Thread wrapper for executor functions. | |
* | |
* @author Jesse Shaffer | |
* @license MIT | |
**/ | |
component accessors=true { | |
property name="fn" setter=false; | |
SoftReference = createObject("java", "java.lang.ref.SoftReference"); | |
/** | |
* Creates an Async runner. | |
* | |
* @fn A function to run in a thread. If a closure is used, any references will be preserved. | |
* @priority Thread priority | |
**/ | |
public Async function init(required fn, priority="normal") { | |
variables.id = createuuid(); | |
variables.fn = fn; | |
variables.priority = priority; | |
variables.promise = javaCast("null",""); | |
return this; | |
} | |
/** | |
* Begin executing. Any arguments passed in are forwarded to the executor. | |
**/ | |
public Promise function run() { | |
promise = new Promise(); | |
thread name = "async-#id#" | |
action = "run" | |
priority = priority | |
fn = SoftReference.init(fn) | |
promise = SoftReference.init(promise) | |
args = SoftReference.init(arguments) | |
{ | |
try { | |
fn = fn.get(); | |
promise.get().resolve(fn(argumentCollection=args.get())); | |
} catch(any e) { | |
promise.get().reject(e); | |
} | |
} | |
return promise; | |
} | |
/** | |
* Joins the async thread and returns the result of the promise. | |
**/ | |
public any function await() { | |
thread action="join" name="async-#id#"; | |
if (!isNull(promise)) { | |
var value = promise.getValue(); | |
promise = javaCast("null",""); | |
return value; | |
} | |
} | |
/** | |
* Get the thread status | |
**/ | |
public string function status() { | |
return threadData().status; | |
} | |
/** | |
* Get the thread's output buffer | |
**/ | |
public string function getOutput() { | |
return threadData().output; | |
} | |
/** | |
* Get the thread data | |
**/ | |
public struct function threadData() { | |
return cfthread[id]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Promise implementation for CFML | |
* | |
* @author Jesse Shaffer | |
* @license MIT | |
**/ | |
component accessors=true { | |
property name="state" setter=false; | |
property name="value" setter=false; | |
PENDING = "PENDING"; | |
FULFILLED = "FULFILLED"; | |
REJECTED = "REJECTED"; | |
/** | |
* Create a promise | |
* | |
* @executor A callback function to begin executing - passed resolve and reject. If an executor is not supplied, you must manually resolve the promise with a value. | |
**/ | |
public function init(executor) { | |
var self = this; | |
var selfVars = variables; | |
state = PENDING; | |
value = javaCast("null",""); | |
handlers = []; | |
// closures to ensure references stay intact | |
resolver = function(result) { | |
self.resolve(result); | |
}; | |
// closures to ensure references stay intact | |
rejecter = function(reason) { | |
self.reject(reason); | |
}; | |
if (!isNull(executor)) | |
this.doResolve(executor, resolver, rejecter); | |
return this; | |
} | |
/** | |
* Immediately fulfill this promise with a value. This is not the prefered method - use resolve() | |
**/ | |
public function fulfill(result) { | |
state = FULFILLED; | |
value = result; | |
handleAll(); | |
} | |
/** | |
* Reject this promise with an error. | |
**/ | |
public function reject(reason) { | |
state = REJECTED; | |
value = reason; | |
handleAll(); | |
} | |
/** | |
* Resolve this promise with a value. | |
**/ | |
public function resolve(result) { | |
try { | |
if (isPromise(result)) { | |
doResolve(result, resolver, rejecter); | |
return; | |
} else { | |
this.fulfill(result); | |
} | |
} catch(any e) { | |
this.reject(e); | |
} | |
} | |
/** | |
* Add callbacks for when this promise is completed. | |
**/ | |
public function done(onFulfilled, onRejected) { | |
addHandler(onFulfilled, onRejected); | |
} | |
/** | |
* Chainble method to add callbacks for when this promise is completed. | |
**/ | |
public function then(onFulfilled, onRejected) { | |
var self = this; | |
return new Promise(function(resolve, reject) { | |
self.done(function(result) { | |
if (!isNull(onFulfilled)) { | |
try { | |
return resolve(onFulfilled(result)); | |
} catch(any e) { | |
return reject(e); | |
} | |
} else { | |
return resolve(result); | |
} | |
},function(reason) { | |
if (!isNull(onRejected)) { | |
try { | |
return resolve(onRejected(reason)); | |
} catch(any e) { | |
return reject(e); | |
} | |
} else { | |
return reject(result); | |
} | |
}); | |
}); | |
} | |
/** | |
* | |
**/ | |
public function catch(onError) { | |
return this.then(function() {}, onError); | |
} | |
// safely executes a function so that this promise can only be fulfilled or rejected | |
private function doResolve(fn, onFulfilled, onRejected) { | |
var done = false; | |
try { | |
fn(function(value) { | |
if (done) return; | |
done=true; | |
onFulfilled(value); | |
}, function(reason){ | |
if (done) return; | |
done=true; | |
onRejected(reason); | |
}); | |
} catch (any e) { | |
if (done) return; | |
done=true; | |
onRejected(e); | |
} | |
} | |
// execute all handlers | |
private function handleAll() { | |
for (handler in handlers) handle(handler); | |
} | |
// queues and executes callback handlers | |
private function handle(handler) { | |
switch(state) { | |
case "PENDING": | |
handlers.push(handler); | |
break; | |
case "FULFILLED": | |
handler.onFulfilled(value); | |
break; | |
case "REJECTED": | |
handler.onFulfilled(value); | |
break; | |
} | |
} | |
// helper method to add a handler | |
private function addHandler(onFulfilled, onRejected) { | |
handlers.append(arguments); | |
} | |
// determine if the value is a promise object | |
private function isPromise(value) { | |
return isObject(value) && (isInstanceOf(value, "org.jdsnet.cfml.Promise") || isInstanceOf(value, "Promise")); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment