Skip to content

Instantly share code, notes, and snippets.

@yoavniran
Last active May 11, 2018 09:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save yoavniran/b6e32b6e46ebc21f9b7f to your computer and use it in GitHub Desktop.
Save yoavniran/b6e32b6e46ebc21f9b7f to your computer and use it in GitHub Desktop.
promise actions - run methods in sequence or parallel using the power of JS Promises
/**
* accepts n methods in sequence as arguments and returns a promise that is resolved
* once all methods finish.
* any argument may be an array of methods instead of a method itself. in this case, the array methods
* will be run in parallel
* any of the passed in methods may return a promise or any other type
*
* the results of the previous methods is handed over to the next method
*
* the last parameter may be an object containing one or both: {context, data}
* context: will be used as the context (this) of the methods called
* data: will be passed in as a parameter to the methods called,
* if data isnt passed, then it will not be passed to the methods called either
*
* the signature of the methods passed is: fn([data, ] results)
*/
function runActions() {
var args = Array.prototype.slice.apply(arguments),
options = _getRunActionsOptions(args),
results = [];
return new Promise(function (resolve, reject) {
var runAction = function (fn) {
var result;
if (_.isArray(fn)) {
fn.push(options);
result = runParallelActions.apply(null, fn);
}
else if (!_.isFunc(fn)) {
throw new TypeError("Utils.runActions - received a non function as action");
}
else {
result = _promisifyReturnValue(fn.apply(options.context, options.data));
}
return result;
};
var runRemainingActions = function (results) {
if (args.length > 0) {
runAction(args.shift(), results)
.then(function (res) {
results.push(res);
runRemainingActions(results);
})
.catch(function (err) {
reject(err);
});
}
else {
resolve(results);
}
};
options.data.push(results);
runRemainingActions(results);
});
}
/**
* accepts n methods as arguments and returns a promise
* that will be resolved once all promises returns from the n methods resolve
* the methods are executed asynchronously !
* any of the passed in methods may return a promise or any other type
*
* the last parameter may be an object containing one or both: {context, data}
* context: will be used as the context (this) of the methods called
* data: will be passed in as a parameter to the methods called
*/
function runParallelActions() {
var args = Array.prototype.slice.apply(arguments),
options = _getRunActionsOptions(args);
return new Promise(function (resolve, reject) {
var ctr = args.length,
actions = [];
var execFn = function (fn) {
if (!_.isFunc(fn)) {
throw new TypeError("Utils.runParallelActions - received a non function as action");
}
ctr -= 1;
actions.push(_promisifyReturnValue(fn.apply(options.context, options.data)));
if (ctr === 0) { //finished running methods
Promise.all(actions)
.then(function (data) {
resolve(data);
})
.catch(function (err) {
reject(err);
});
}
};
_.each(args, function (fn) { //returns control to the calling method as soon as possible
setTimeout(execFn.bind(null, fn), 0);
});
});
}
/**
* finds if the last argument is an options object
* removes it from the array and returns it
* @private
*/
function _getRunActionsOptions(args) {
var options = args.pop(),
data =[];
if (_.isSimpleObject(options)) { //check if got options as last argument
if (!_.isUndefined(options.data)) {
data.push(_.copy(options.data));
}
options.data = data;//data is array that will spread into each action method
}
else {
args.push(options); //probably an action method, put it back
options = {data: data};
}
return options;
}
function _promisifyReturnValue(value) {
var isPromise = (!_.isUndefined(value) && _.isFunc(value.then) && _.isFunc(value.catch));
return (isPromise ? value : Promise.resolve(value));
}
System.import("utils").then(function(utils){
utils.runActions(
function(){console.log("running #1");
return new Promise(function(resolve){
setTimeout(resolve.bind(this, "aaa"), 200);
});
},
function(){console.log("running #2"); return "bbb";},
[
function(){console.log("running #3");
return new Promise(function(resolve){
setTimeout(resolve.bind(this, "ccc"), 200);
});
},
function(){console.log("running #4");
return new Promise(function(resolve){
setTimeout(resolve.bind(this, "ddd"), 500);
});
},
],
function(){console.log("running #5"); return "eee";}
)
.then(function(data){
console.log("finished! ", data);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment