Thursday, January 29, 2015

jQuery .ensure() Function to Wait for a Condition Before Executing Code

Overview

This is a very nice little plugin I wrote for jQuery to handle situations that don't have callbacks.  You can do this manually in code by adding a timeout but what if the function takes to long, or what make users with a faster device wait for slower devices by over estimating the timeout.

Please note that most libraries have their own callbacks and jQuery had a $().done() mechanism that will do this for common functions and libraries.  This solution is for those odd ball code bits that don't or that you don't have control over.

It takes a few pieces of information:
  • selector
    • The root jQuery selector you are binding to, it will become the this in the isValid call.
    • If your waiting for a new item to be created do $(document).ensure(selector).done().
  • isValid (default $(this).exists())
    • This is the function that is run each iteration.
    • It should be optimized and return a bool result.  Ie don't walk the DOM every time, select the proper parent node into a variable or pass as the selector.
  • delay (default 500 milliseconds)
    • Miliseconds between each iteration.
  • tries (default 0)
    • Maximum number of times to try, 0 for unlimited.

Examples:

$(document).ensure(function() {
return $("p.test").exists();
}).done(function() {
console.log("p.test exisits.");
});
$("body").ensure(function() {
return $("a", this).exists();
}).done(function(self) {
$("a", self).toggle();
});
$(selector).ensure({
delay: 500,
tries: 0,
isValid: function() {
return $(this).is(".loaded");
}
}).done(function(self) {
console.log("done loading resource");
}).fail(function() {
console.log("failed to load resource");
});
view raw ensure-demo.js hosted with ❤ by GitHub

Source Code:

/* Add $().exists() helper method.
Usage:
$(selector).exists()
*/
jQuery.fn.exists = function(){return this.length>0;}
/* Add $().ensure() helper method that ensures conditions pass before executing.
This will re-check the conditions untill they are meet or the limit is reached.
Usage:
$(document).ensure(function() {
return $("p.test").exists();
}).done(function() {
console.log("p.test exisits.");
});
$("body").ensure(function() {
return $("a", this).exists();
}).done(function(self) {
$("a", self).toggle();
});
$(selector).ensure({
delay: 500,
tries: 0,
isValid: function() {
return $(this).is(".loaded");
}
}).done(function(self) {
console.log("done loading resource");
}).fail(function() {
console.log("failed to load resource");
});
*/
jQuery.fn.ensure = function(options) {
if(typeof(options) == "function")
options = { isValid: options };
if(typeof(options) == "string") {
var selector = options;
options = { isValid: function() {
return $(selector, this).exists();
}};
}
options = $.extend({
delay: 500,
tries: 0,
isValid: function() {
return $(this).exists();
}
}, options);
var self = this;
var ensurePromise = new $.Deferred();
var loopCount = 0;
var looper = setInterval(function () {
loopCount++;
if (options.isValid.apply(self)) {
clearInterval(looper);
ensurePromise.resolve(self);
} else if(options.tries > 0 && loopCount >= options.tries) {
clearInterval(looper);
ensurePromise.reject(self);
}
}, options.delay);
return ensurePromise;
};