Created
January 31, 2012 23:41
-
-
Save Fordi/1713856 to your computer and use it in GitHub Desktop.
Javascript Workflow Processor
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
(function () { | |
var sayHi = function (token) { | |
if (!token.progress) token.progress = {}; | |
token.progress.saidHi = (token.progress.saidHi||0) + 1; | |
//true == simply proceed; false == reject workflow | |
return true; | |
}, | |
shakeIt = function (token, next) { | |
token.progress.shook = (token.progress.shook||0) + 1; | |
next('right'); | |
}, | |
fakeIt = function (token, next) { | |
token.progress.faked = (token.progress.faked||0) + 1; | |
next(true); | |
}, | |
breakIt = function (token) { | |
token.progress.broken = (token.progress.broken||0) + 1; | |
return true; | |
}, | |
bakeIt = function (token) { | |
token.progress.baked = (token.progress.baked||0) + 1; | |
return true; | |
}; | |
var wf = new WorkFlow( //Workflow | |
sayHi, | |
[ //Parallel | |
[ //Sequence | |
shakeIt, | |
{ //Branch | |
left: fakeIt, | |
right: [ //Parallel | |
breakIt, | |
shakeIt | |
] | |
} | |
], | |
bakeIt | |
], | |
sayHi, | |
[ sayHi, fakeIt, breakIt ], //Parallel | |
sayHi | |
); | |
var then = +new Date(); | |
wf({ token: 'A' }) | |
.done(function (token) { | |
var now = +new Date(); | |
console.log("Running time: ", now-then, 'ms'); | |
}) | |
.progress(function (token) { | |
var prg = token.progress; | |
console.log([ | |
"Said Hi "+(prg.saidHi||0), | |
"Shook "+(prg.shook||0), | |
"Faked "+(prg.faked||0), | |
"Broken "+(prg.broken||0), | |
"Baked "+(prg.baked||0) | |
].join('; ')); | |
}); | |
}()); |
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
/** | |
* WorkFlow processor | |
* | |
* @author Bryan Elliott <[email protected]> | |
* @info | |
* Create a managed, asynchronous workflow, with a corresponding | |
* jQuery.Promise. Each step may be synchronous, or asynchronous, meaning | |
* user-invoked and AJAX calls can be integrated into a sequential workflow. | |
* | |
* | |
* @class WorkFlow(...) | |
* @info Constructor for creating #WorkFlow objects | |
* @type #Action A Function, #Parallel, #Branch, or #WorkFlow | |
* @type #Sequence A Function or an array of #Actions; the first #Action in a #Sequence MUST NOT be a #Branch | |
* @type #Parallel An array of #Sequences | |
* @type #Branch An object containing a set of name to #Sequence relationships | |
* @rest #Action taken together, the sequence of actions to perform | |
* @return Function An executable #WorkFlow | |
* | |
* @function #WorkFlow(token) | |
* @info An instance of WorkFlow | |
* @param Object token a state object to be passed through the steps of the #WorkFlow | |
* @return jQuery.Promise Resolved when the #WorkFlow is either resolved or rejected. | |
* | |
* @depends jQuery.Deferred | |
**/ | |
var WorkFlow = (function () { | |
var getFnName = !!(function getFnName() {}).name ? function (f) { | |
if (f.original instanceof Function) | |
return arguments.callee(f.original()); | |
return f.name; | |
} : function (f) { | |
if (f.original instanceof Function) | |
return arguments.callee(f.original()); | |
return f.toString().replace(/function\s*([^\( ]*)[\s\S]*$/, '$1'); | |
}; | |
var WorkFlow = function () { | |
var inst = this, | |
sequence = Array.prototype.slice.call(arguments); | |
return function Flow(token) { | |
if (!(this instanceof arguments.callee)) { | |
return new arguments.callee(token); | |
} | |
var flow = this; | |
flow.sequence = Array.prototype.slice.call(sequence); | |
flow.processSequence = function () { | |
WorkFlow.prototype.processSequence.apply(flow, arguments); | |
}; | |
WorkFlow.prototype.execute.apply(this, arguments); | |
return this.deferred.promise(); | |
}; | |
}; | |
WorkFlow.prototype.resolve = function (token) { | |
this.deferred.resolveWith(this, token); | |
}; | |
WorkFlow.prototype.reject = function (token) { | |
this.deferred.rejectWith(this, token); | |
}; | |
WorkFlow.prototype.processSequence = function (sequence, sequenceComplete) { | |
if (sequence instanceof Function) | |
sequence = [ sequence ]; | |
var i, | |
inst = this, | |
promise = this.deferred.promise(), | |
methods = []; | |
if (sequence[0] instanceof Object && !(sequence[0] instanceof Array) && !(sequence[0] instanceof Function)) | |
throw new Error("Branches MUST NOT be the first item in a sequence"); | |
for (i=0; i<sequence.length; i++) (function (item, index) { | |
var next = function (result) { | |
inst.deferred.notifyWith(promise, [inst.token]); | |
inst.lastResult = result; | |
if (result===false) { | |
inst.deferred.rejectWith(promise, [inst.token]); | |
return; | |
} | |
index++; | |
if (index < methods.length) { | |
methods[index].call(); | |
return; | |
} | |
if (index === methods.length) { | |
inst.deferred.notifyWith(promise, [inst.token]); | |
sequenceComplete(inst); | |
return; | |
} | |
throw new Error("Workflow reached an illegal state"); | |
}; | |
if (item instanceof Array) { | |
var parallel = item, | |
count = parallel.length; | |
item = function (token, proceed) { | |
for (var i=0; i<parallel.length; i++) (function (seq, index) { | |
setTimeout(function () { | |
inst.processSequence(seq, function () { | |
if (--count > 0) { | |
return; | |
} | |
proceed(); | |
}); | |
},1); | |
}(parallel[i], i)); | |
}; | |
} | |
if (item instanceof Object && !(item instanceof Function)) { | |
var branch = item, | |
item = function (token, proceed) { | |
var seq = branch[inst.lastResult]; | |
if (!seq) | |
throw new Error("Could not switch to branch \""+inst.lastResult+"\""); | |
inst.processSequence(seq, function () { | |
proceed(); | |
}); | |
} | |
} | |
methods[index] = function () { | |
var ret = item.call(inst.deferred, inst.token, next); | |
if (typeof ret !== 'undefined') | |
next(ret); | |
}; | |
}(sequence[i], i)); | |
methods[0](); | |
}; | |
WorkFlow.prototype.execute = function (token, next) { | |
if (!!this.token) | |
throw new Error("Cannot execute workflow already in progress"); | |
this.token = token || {}; | |
this.deferred = new jQuery.Deferred(); | |
WorkFlow.prototype.processSequence.call(this, this.sequence, function (inst) { | |
inst.deferred.resolveWith(inst.deferred.promise(), [inst.token]); | |
if (next instanceof Function) | |
next(true); | |
}); | |
return this.deferred.promise(); | |
}; | |
return WorkFlow; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment