Hello, I'm Nick Fitzgerald. This is my weblog. You can also check out my shared items from Reader, moments on Twitter, and code on GitHub. Feel free to contact me about whatever.
July 19th, 2010
Angus Croll just wrote a blog post on "function requests [where] the function only gets invoked after you’ve finally stopped requesting it". This started out as a comment over there, but quickly grew a little long, so I am just going to post it here. Go read his post to get a little context, it's worth your time.
I have used this pattern multiple times, but I never realized I should generalize it. Kudos, Angus! Great code; great read.
Now to the nitpicks :)
Angus implemented this utility as a prototype constructor, but my opinion is that using a closure is more elegant. It takes less lines of code to implement, and more importantly, less lines of code to use.
// Angus' version, with a bug
var resizeMonitor = new FunctionGuard(function (event) {
if (typeof window.onresizeend === "function")
window.onresizeend(event);
}, 100);
window.onresize = resizeMonitor.run;
But, it's actually worse than that to use, because you must bind the run
method to the resizeMonitor object. If you don't then this will refer to the
global object, rather than the resizeMonitor.
// Bug-free version
var resizeMonitor = new FunctionGuard(function (event) {
if (typeof window.onresizeend === "function")
window.onresizeend(event);
}, 100);
window.onresize = function () {
resizeMonitor.run.apply(resizeMonitor, arguments);
};
Making this utility a function that returns another function with state hidden in a closure is easier for end-users.
// Same example, using my version
window.onresize = doLastCallAfter(100, function (event) {
if (typeof window.onresizeend === "function")
window.onresizeend(event);
});
Simulating an onresizeend event, was definitely a cool example. The first
thing that I thought of was to automatically logout an inactive user, and
protect their privacy.
var logoutAfterInactivity = doLastCallAfter(600000, function () {
window.location.href = "http://example.com/logout";
});
document.body.onmousemove = document.body.onkeydown = logoutAfterInactivity;
And finally, here is my implementation.
function doLastCallAfter(quietTime, fn, context /*, fixed args */) {
context = context || this;
function slice(thing, i) {
return Array.prototype.slice.call(thing, i || 0);
}
var timer, fixedArgs = slice(arguments, 3);
return function (/* dynamic args */) {
var dynamicArgs = slice(arguments);
if (timer !== undefined) clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, fixedArgs.concat(dynamicArgs));
}, quietTime);
};
}
Awesome utility, Angus!
Source Map Specification Discussion Mailing List on February 28th, 2013
Regarding "Dynamic Source Maps" on January 22nd, 2013
Update on Firefox and Source Maps on July 30th, 2012
Source Code Cartography on December 23rd, 2011
Pycco Needs a Loving Home on August 17th, 2011
Operational Transformation Part 2: Operations on April 5th, 2011
Operational Transformation: An Introduction on March 26th, 2011
JavaScript Timer Congestion on March 8th, 2011
OOP The Good Parts: Message Passing, Duck Typing, Object Composition, and not Inheritance on December 31st, 2010
Pythonic JavaScript Methods on November 16th, 2010