Last active
May 6, 2024 12:33
-
-
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
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
/** | |
* 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)); | |
} |
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
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