A lot of JavaScript libraries (notably jQuery) use chaining, which allows the reduction of this:
var foo = $(".foo");
foo.stop();
foo.show();
foo.animate({ top: 0 });
to this:
$(".foo").stop().show().animate({ top: 0 });
With proper formatting, I think this is quite a nice syntactic capability. However, I often see a pattern which I don’t particularly like, but appears to be a necessary evil in non-blocking models. This is the ever-present nesting of callback functions:
$(".foo").animate({
top: 0,
}, {
callback: function () {
$.ajax({
url: 'ajax.php',
}, {
callback: function () { ... }
});
}
});
And it never ends. Even though I love the ease non-blocking models provide, I hate the odd nesting of function literals it forces upon the programmer.
I’m interesting in writing a small JS library as an exercise, and I’d love to find a better way to do this, but I don’t know how it could be done without feeling hacky. Are there any projects out there that have resolved this problem before? And if not, what are the alternatives to this ugly, meaningless code structure?
Just because you can use anonymous functions (lambdas) for this kind of construct doesn’t mean you have to. Just like it’s good practice to pull complex value construction syntax out of a function call and into its own variable, you can do the same with your function literals. Observe:
// Your example:
$(".foo").animate({
top: 0,
}, {
callback: function () {
$.ajax({
url: 'ajax.php',
}, {
callback: function () { ... }
});
}
});
// Can be rewritten as:
var animateProps = { top: 0 };
var animateCallback = function() {
var ajaxProps = { url: 'ajax.php'; }
var ajaxCallback = function () { ... }
$.ajax(ajaxProps, {callback: ajaxCallback});
};
$(".foo").animate(animateProps, {callback: animateCallback});
// Or even, depending on what you do inside the ajax callback:
var animateProps = { top: 0 };
var ajaxProps = { url: 'ajax.php'; }
var ajaxCallback = function () { ... }
var animateCallback = function() {
$.ajax(ajaxProps, {callback: ajaxCallback});
};
$(".foo").animate(animateProps, {callback: animateCallback});
And all the ugly nested syntax is gone!
Note, however, that the nested-anonymous-function style isn’t all that bad; after all, if your function calls follow a logical structure, then so will the function call nestings. Example:
doAsyncSomething(
function(successData){
alert("All is well!");
},
function(errorMessage){
alert("Oh no! Something went wrong! Namely: " + errorMessage);
});
In this example, the code nesting follows the logical structure quite nicely.
I personally like this style, as an added bonus you get named functions, which is really helpful for debugging:
$(".foo").animate({
top: 0,
}, {
callback: animateCallback
});
function animateCallback() {
$.ajax('ajax.php', ajaxCompleted);
}
function ajaxCompleted() {
// ...
}
In my experience, more often than not you don’t actually want to have anonymous functions. I found myself time and time again triggering events on buttons for example, because I bound a particular function as an event listener, instead of an independent function.