-
Notifications
You must be signed in to change notification settings - Fork 7
Flow Use Cases
Below are use-case scenarios for including Flow in your work. Feel free to add your own use case (real or theorized) by editing this page, adding a task, or emailing the author.
- Preventing Consecutive Execution
- Redirecting Execution
- Delaying the Call-Chain
- Restricting Execution
- Denying External Calls
- Altering Arguments
- Sequencing Executions Dynamically
- Executing Prerequisites
- Reverting Actions
- Debouncing Event Handlers
- Avoiding Stack-Overflows
In order to prevent consecutive executions, functions require variables (and the management thereof).
var isSubmitting = 0;
var submitForm = function () {
if (!isSubmitting) {
isSubmitting = 1;
document.myForm.submit();
// console.log('submitting form');
} else {
// console.log('already submitting');
}
};
submitForm(); // -> submitting form
submitForm(); // -> already submittingFlow remembers it's position in a program, such that an entered state will not be re-entered when invoked consecutively.
var submitForm = new Flow({
_in: function () {
document.myForm.submit();
// console.log('submitting form');
this.wait();
},
_main: function () {
// console.log('already submitting');
}
});
submitForm(); // -> submitting form
submitForm(); // -> already submittingNote: The "_main" component above is only defined for illustrative purposes. (The wait() method is used to prevent executing the "_main" component, during the first call.)_
In order to redirect execution, a group of functions use the same branching logic.
var myApp = {
isAuth: 0,
login: function () {
if (this.isAuth) {
this.run();
} else {
this.isAuth = 1;
// console.log('authenticated user');
this.run();
}
},
run: function () {
if (!this.isAuth) {
this.login();
} else {
// console.log('running app');
}
}
};
myApp.run(); // -> authenticated user
// -> running appFlow traverses a program sequentially (e.g., from first to last), such that any state can intercept accessing it's older siblings.
var myApp= new Flow({
login: function () {
_main: function () {
this.vars('isAuth', 1);
// console.log('authenticated user');
},
_over: function () {
if (!this.vars('isAuth')) {
this.go('@self');
}
}
},
run: function () {
// console.log('running app');
}
});
myApp.run(); // -> authenticated user
// -> running appNote: Both examples auto-authenticate the user, for illustrative purposes only.
In order to delay the call-chain (after delaying a statement), functions must synchronize their execution by duplicating the delay or employing a call-back scheme.
function task() {
var call_1 = function (callback) {
setTimeout(function () {
// console.log('call 1 (delayed)');
callback();
}, 1000);
},
call_2 = function () {
// console.log('call 2');
},
call_3 = function () {
// console.log('call 3');
};
call_1(call_2);
setTimeout(call_3, 1000);
};
task(); // -> call 1 (delayed)
// -> call 2
// -> call 3Flow calls functions by traversing their state, such that delaying navigation delays their execution.
var task = new Flow({
_main: function () {
this.go('call_1','call_2','call_3');
},
call_1: function () {
this.wait(1000);
// console.log('call 1 (delayed)');
},
call_2: function() {
// console.log('call 2');
},
call_3: function() {
// console.log('call 3');
}
});
task(); // -> call 1 (delayed)
// -> call 2
// -> call 3In order to restrict execution, the called function must have logic that determines when it may execute.
var myApp = {
onlyModalClicks: 0,
modal: {
show: function () {
this.onlyModalClicks = 1;
// console.log('modal visible');
},
hide: function () {
this.onlyModalClicks = 0;
// console.log('modal hidden');
}
},
exit: function () {
if (!this.onlyModalClicks) {
// console.log('closing app');
} else {
// console.log('restricted call');
}
}
};
function showModalBtn() {
myApp.modal.show();
}
function hideModalBtn() {
myApp.modal.hide();
}
function exitAppBtn() {
myApp.exit();
}
showModalBtn(); // -> modal visible
exitAppBtn(); // -> restricted call
hideModalBtn(); // -> modal hidden
exitAppBtn(); // -> closing appFlow tracks where it is in your program, such that you may restrict external functions from targeting states outside the current branch (i.e., a state and it's descendants).
var myApp = new Flow({
modal: {
_restrict: 1,
show: function () {
// console.log('modal visible');
},
hide: function () {
this.go(1);
// console.log('modal hidden');
}
},
exit: function () {
// console.log('closing app');
}
});
function showModalBtn() {
myApp.modal.show();
}
function hideModalBtn() {
myApp.modal.hide();
}
function exitAppBtn() {
var flowMoved = myApp.exit();
if (!flowMoved) {
// console.log('restricted call');
}
}
showModalBtn(); // -> modal visible
exitAppBtn(); // -> restricted call
hideModalBtn(); // -> modal hidden
exitAppBtn(); // -> closing appNote: The flowMoved variable and if statement, from the second set of handler functions, are only used for continuity purposes.
In order to deny external calls, external functions must use internal logic.
var myApp = {
denyUI: 0,
init: function () {
this.splash();
},
splash: function () {
// console.log('showing splash screen');
setTimeout(function () {
myApp.run();
}, 3000);
},
run: function () {
myApp.denyUI = 0;
// console.log('running app (3 seconds later)');
}
};
function runAppBtn() {
if (!myApp.denyUI) {
myApp.init();
} else {
// console.log('denied call');
}
}
runAppBtn(); // -> showing splash screen
runAppBtn(); // -> denied call
// -> running app (3 seconds later)Flow distinguishes when calls are external to it's program, such that they may be denied dynamically.
var myApp = new Flow({
init: function {
this.go('/run');
},
splash: {
_over: function () {
this.go('@self');
},
_main: function () {
this.lock(1);
this.wait(3000);
// console.log('showing splash screen');
}
},
run: function () {
this.lock(0);
// console.log('running app (3 seconds later)');
}
});
function runAppBtn() {
if (!myApp.init()) {
// console.log('denied call');
}
}
runAppBtn(); // -> showing splash screen
runAppBtn(); // -> denied call
// -> running app (3 seconds later)Note: The if statement, from the second handler function, is only used for continuity purposes.
In order to alter arguments, the target function must be called by a closure, which performs the alteration(s).
var myApp = {
greet = function (str) {
// console.log(str);
}
},
closureGreeter = function (fnc) {
return function () {
if (arguments[0] === 'World') {
// console.log('augmenting arguments');
arguments[0] = 'Hello!';
}
return fnc.apply(this, arguments);
}
},
myApp.greet = closureGreeter(myApp.greet);
myApp.greet('World'); // -> augmenting arguments
// -> Hello!Flow receives arguments before passing them to the _main function of a target state, such that any state's functions may alter them before hand.
var myApp = new Flow({
greet: {
_in: function () {
if (this.args(0) === 'Hello') {
// console.log('augmenting arguments');
this.args(0, 'World!');
}
},
_main: function (str) {
// console.log(str);
}
}
});
myApp.greet('World'); // -> augmenting arguments
// -> Hello!In order to sequence executions dynamically, functions must be designed to accept and execute callbacks (other functions).
var myApp = {
one: function (cb) {
// console.log('called one');
cb();
},
two: function (cb) {
// console.log('called two (delayed)');
window.setTimeout(cb, 1000);
},
three: function (cb) {
// console.log('called three');
}
};
myApp.three(
function () {
myApp.two(myApp.one);
}
); // -> called three
// -> called two (delayed)
// -> called oneFlow oversees all functions of the states in it's program, such that states may be targeted (for traversal) centrally, regardless of each function's design.
var myApp = new Flow({
one: function () {
// console.log('called one');
},
two: function () {
// console.log('call two (delayed)');
this.wait(1000);
},
three: function () {
// console.log('called three');
}
}, true);
myApp.go('three','two','one'); // -> called three
// -> called two (delayed)
// -> called oneIn order to execute prerequisites, a dependent function must be called within a closure, which handles calling the prerequisite function.
var preClosure = function (pre, fnc) {
return function () {
pre();
fnc();
}
},
myModal = {
init: function () {
// console.log('initialize modal');
},
login: {
init: function () {
// console.log('load and show login modal');
},
focus: function () {
// console.log('focus on username field');
},
hide: function () {
// console.log('hide login modal');
}
},
destroy: function () {
// console.log('destroy modal');
}
};
myModal.login.focus = preClosure(myModal.login.init, myModal.login.focus);
myModal.login.focus = preClosure(myModal.init, myModal.login.focus);
myModal.destroy = preClosure(myModal.login.hide, myModal.destroy);
myModal.login.focus(); // -> initialize modal
// -> load and show login modal
// -> focus on username field
myModal.destroy(); // -> hide login modal
// -> destroy modalNote: For demonstrative purposes, this example only defines prerequisites of the called methods.
Flow uses simple encapsulation and specially keyed components, to identify and execute functions when entering and exiting states of the program.
var myModal = new Flow({
_in: function () {
// console.log('initialize modal');
},
login: {
_in: function () {
// console.log('load and show login modal');
},
focus: function () {
// console.log('focus on username field');
},
_out: function () {
// console.log('hide login modal');
}
},
destroy: function () {
this.target(0);
},
_out: function () {
// console.log('destroy modal');
}
});
myModal.login.focus(); // -> initialize modal
// -> load and show login modal
// -> focus on username field
myModal.destroy(); // -> hide login modal
// -> destroy modalIn order to revert actions, counter-functions must be explicitly called to undo what has occurred.
var badApp = {
inited: 0,
origTitle: '',
init: function () {
if (!this.inited) {
this.inited = 1;
this.setNameSpace();
}
},
setNameSpace: function () {
// console.log('setting global');
window.badGlobal = {};
},
changeTitle: function () {
if (!this.inited) this.init();
if (!this.origTitle) this.origTitle = document.title;
// console.log('changing title');
document.title = 'foo bar';
},
revert: function () {
this.unChangeTitle();
this.unInit();
},
unchangeTitle: function () {
// console.log('reverting title');
document.title = this.origTitle;
this.origTitle = '';
}
unInit: function () {
this.inited = 0;
this.unsetNamespace();
},
unsetNamespace: function () {
// console.log('removing global');
delete window.badGlobal;
},
};
badApp.changeTitle(); // -> setting global
// -> changing title
badApp.reset(); // -> reverting title
// -> removing globalFlow groups and isolates functionality, such that each state may revert it's own actions when exited.
var badApp = new Flow({
_in: function () {
// console.log('setting global');
window.badGlobal = {};
},
changeTitle: {
_var: 'origTitle',
_in: function () {
this.vars('origTitle', document.title);
},
_main: function () {
// console.log('changing title');
document.title = 'foo bar';
},
_out: function () {
// console.log('reverting title');
document.title = this.vars('origTitle');
}
},
revert: function () {
this.target(0);
},
_out: function () {
// console.log('removing global');
delete window.badGlobal;
}
});
badApp.changeTitle(); // -> setting global
// -> changing title
badApp.reset(); // -> reverting title
// -> removing globalIn order to debounce event handlers, functions must manage timeout-identifiers that delay calls.
var timeoutId,
handler = function (e) {
// console.log('handling resize event');
},
debounceResize = function (e) {
clearTimeout(timeoutId);
timeoutId = setTimeout(
function () {
handler(e);
},
500
);
// console.log('debouncing event');
};
window.addEventListener('resize', debounceResize, false);Flow provides a single timing construct, such that setting a delay automatically clears the previous one.
var debounceResize = new Flow({
_main: function () {
this.go('handler');
this.wait(500);
// console.log('debouncing event');
},
handler: function (e) {
// console.log('handling resize event');
}
});
window.addEventListener('resize', debounceResize, false);In order to avoid stack-overflows, recursive routines must use iteration and nested functions.
function fibonacci(n, prev, cur) {
var fibCalc = function () {
var tmp = prev;
prev = cur;
cur += tmp;
};
while (n--) {
// console.log(cur);
fibCalc();
}
}
fibonacci(1000, 0, 1); // -> 1
// -> 1
// -> 2
// -> 3
// -> 5
// -> ...Flow navigates states instead of calling functions, such that recursive iterations do not increase the call-stack.
var fibonacci = new Flow({
_main: function (n, prev, cur) {
// console.log(cur);
if (n--) {
this.target('@self', n, cur, cur + prev);
}
}
}
fibonacci(1000, 0, 1); // -> 1
// -> 1
// -> 2
// -> 3
// -> 5
// -> ...