Timeout loops
RE: Doug Crockford’s talk at http://developer.yahoo.com/yui/theater/video.php?v=crockonjs-3
In slide 43, there’s a fade out algorithm that looks like this. He’s using it to illustrate closure – in this case, the variables ‘level’ and ‘dom’:
function fade(id) {
var dom = document.getElementById(id),
level = 1;
function step() {
var h = level.toString(16);
dom.style.backgroundColor = '#FFFF' + h + h;
if (level < 15) {
level += 1;
setTimeout(step, 100);
}
}
setTimeout(step, 100);
}
Now this struck me as strange - mutation within a recursive algorithm. This inspired me to do a couple of rewrites that avoid that, whilst providing some interesting abstractions that separate concerns.
1. Separating out the notion of a delayed function.
timedOut is a higher-order that wraps a function in a timeout loop. This makes the code feel more like a normal recursive algorithm. Notice the step(level + 1) replaces the mutation.
function timedOut(fn, delay) {
return function() {
var args = arguments;
setTimeout(function() {
return fn.apply(this, args);
}, delay);
};
}
function fade(id) {
var dom = document.getElementById(id);
var step = timedOut(function(level) {
var h = level.toString(16);
dom.style.backgroundColor = '#FFFF' + h + h;
if (level < 15) {
step(level + 1);
}
}, 100);
step(1);
}
2. Separating out the notion of a timeout loop.
I really like this one. It clearly separates the rendering logic from the mechanism that recurses n times.
function timeoutLoop(fn, delay, start, end) {
var t = function(current) {
setTimeout(function() {
fn(current);
if (current < end) {
t(current + 1);
}
}, delay);
};
t(start);
}
function fade(id) {
var dom = document.getElementById(id);
var step = function(level) {
var h = level.toString(16);
dom.style.backgroundColor = '#FFFF' + h + h;
};
timeoutLoop(step, 100, 1, 15);
}