-
Notifications
You must be signed in to change notification settings - Fork 8
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 submitting
Flow 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 submitting
Note: 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 app
Flow 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 app
Note: 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 3
Flow 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 3
In 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 app
Flow 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 app
Note: 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 one
Flow 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 one
In 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 modal
Note: 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 modal
In 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 global
Flow 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 global
In 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
// -> ...