fansipans' recent posts
10/10/2008 - [ javascript programming learning functional ] - posted by fansipans
Seeing a complaint about function chaining / nesting with setTimeout made me think that there had to be a better way to take advantage of JavaScript's functional abilities.
The issue at hand was how to take a number of functions, and execute them serially, with a delay between functions. The least flexible and most trivial implementation would be something like:
function() {
do_something();
setTimeout(function() {
do_something_else();
setTimeout(function() {
do_something_other()
...
}, 1000);
}, 1000);
}
It's an unusual problem for people not versed in event-driven programming, and those of us working in moderately crippled event-driven environments like the glorious modern Web Browser. The issue is that the functions passed to setTimeouts float off into space until their time runs out, and setTimeout immediately returns. So it's not as simple as iterating through the functions, the calls need to be nested - or ideally - chained.
So after banging out some code, a good amount of refactoring, and getting a better feel for closures and scope in JavaScript, I came up with executeFunctions().
executeFunctions(funcs, transitionFunc) takes two arguments:
funcs is an Array that contains Arrays with two indices, 0 and 1. This two value Array contains the Function to execute, and an Array of arguments to pass to the Function, respectively.
transitionFunc is optional. It's simply a Function that must accept one argument (a Function). If specified, transitionFunc is called each time before the functions in funcs are executed. If transitionFunc executes the function it was given, then funcs will continue to be executed. If not, execution stops. If not specified, all the functions in funcs will be executed in the order they appear.
What does this mean? It means the setTimeout example specified above can be executed from a simple Array of functions with the following extension to executeFunctions:
function executeFunctionsWithTimeout(funcs, timeout) {
var transitionByTimeout = function(f) {
window.setTimeout(f,timeout);
};
executeFunctions(funcs, transitionByTimeout);
}
This is still a recursive solution to a recursive problem, but it lets the user define the code in a much more manageable way. If I let myself use jQuery I'd probably setup a queue of functions and use events to fire them off and detect completion (to fire off the next event), so that each function wasn't actually invoking the next, but this code is trivial, and handles blocking and detached transitions with ease.
I also had the Function Efficiency Considerations section of Mozilla's Core JavaScript Reference gently reminding me to be careful with closures as they can get tricky with garbage collection. I want to say that executeFunctions is ok in this regard, since it's not returning anything into global or any other persistent scope, but I'm still a little new to JavaScript to say for sure.
The demos of executeFunctions() include the use of setTimeout, mouseOver, confirm, and no transitions.
Tested in FF3, IE6, IE7, and Safari.
| comment on this post |