delayedCallback(function(){ … }, delay);

hola, Amigos! it’s been a long time since I rapped at ya!

so, i’ve been doing plenty, i’m just not chatty about it. i built a Ruby migration framework using bundler, pry, spreadsheet, sequel and mp3info to build a JSON document version of my SEB Broadcast database. next up is some node.js to serve it up, then some RequireJS, mustache (?) & jQuery goodness to spiff up the SEB site

but in the meanwhile, i wrote this little gem at work:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// returns a function that will invoke the callback 'delay' ms after it is last called
// eg. invoke the callback 500ms after the last time a key is pressed
// based on http://stackoverflow.com/questions/2219924/idiomatic-jquery-delayed-event-only-after-a-short-pause-in-typing-e-g-timew
// but fixes the following:
// (a) returns a unique timer scope on every call
// (b) propagates context and arguments from the last call to the returned closure
// and adds .cancel() and .fire() for additional callback control
// then of course, you could use underscore's _.debounce
// it doesn't have the .cancel and .fire, but you may not care :)
function delayedCallback(callback, delay) {
return (function(callback, delay) { // (2) receives the values in scope
var timer = 0; // (3a) a scoped timer
var context, args; // (3b) scoped copies from the last invocation of the returned closure
var cb = function() { callback.apply(context, args); } // (3c) called with proper context + arguments
var dcb = function() { // (4) this closure is what gets returned from .delayedCallback
context = this;
args = arguments;
window.clearTimeout(timer);
timer = window.setTimeout(cb, delay); // (5) only fires after this many ms of not re-invoking
};
dcb.cancel = function() { window.clearTimeout(timer); }; // (6a) you can cancel the delayed firing
dcb.fire = function() { this.cancel(); cb(); }; // (6b) or force it to fire immediately
return dcb;
})(callback, delay); // (1) capture these values in scope
}

yes, i know. so it turns out that i didn’t know about underscore’s _.debounce() when i wrote it. eh. so much for DRY :)

still — i’m glad i thought it through. to me, this implementation captures the most powerful aspects of ECMAScript itself:

  • scope-capturing closures
  • specifiable function context
  • freestyle properties on Object instances
  • single-threading (look ma, no synchronize { ... } !)

anyway. bla dee blah. this post also gave me the incentive to start embedding gists in my blog. nice helper widget, dflydev !

peace out