Escaping Callback Hell!
Because a lot of the things we do with Javascript tend to
happen asynchronously, it's easy to get caught up in callback
hell. Not only does this look ugly but it makes our code hard
to reason about as the logical flow becomes obscured. Futures
to the rescue!
Futures, Deferreds, and Promises are all (more-or-less)
different names for the same idea. jQuery has included a
deferred implementation since 1.5 - the code samples below do
the same thing but demononstrate some of the advantages of
using Promises to control flow where previously you may have
used callbacks.
Futures aren't just to manage really complicated code -
fairly straightforward things you may typically want to do
as a jQuery developer (grab two lists of tweets, add a new
tweet on the page every few seconds) might also benefit from
a sprinkling of deferreds!
Read the blog post to learn more »
$(function () {
var template = Handlebars.compile($("#tweet-template").html());
var url1 = "http://search.twitter.com/search.json?callback=?&q=@marakana";
var url2 = "http://search.twitter.com/search.json?callback=?&q=@simeonfranklin";
var max_tweets = 2;
var $tweets = $("#tweets");
var handler = function (e) {
$tweets.children().remove();
$.getJSON(url1, function (data1) {
$.getJSON(url2, function (data2) { // Wait - did I just make my ajax calls serial?
var list = data1.results.concat(data2.results);
list.sort(function (a, b) { return a.id - b.id; });
var adder = function () {
var tweet = list.pop();
var html = $.trim(template(tweet));
var $tweet = $(html);
$tweets.append($tweet);
$tweet.fadeIn(100, function () {
setTimeout(function () {
if ($tweets.children().length >= max_tweets) {
$tweets.children().first().remove();
}
if (list.length > 0) {
adder();
}
}, 2000);
});
};
adder();
});
});
e.preventDefault();
};
$("#launch-cb").click(handler);
}); // AUGH!!!! the pyramid of doom!
$(function () {
var template = Handlebars.compile($("#tweet-template").html());
var url1 = "http://search.twitter.com/search.json?callback=?&q=@marakana";
var url2 = "http://search.twitter.com/search.json?callback=?&q=@simeonfranklin";
var max_tweets = 2;
var $tweets = $("#tweets");
$tweets.children().remove();
var sleep = function (timeout) {
var d = new $.Deferred();
setTimeout(d.resolve, timeout);
return d.promise();
};
var sleep3 = function () {return sleep(3000); };
var adder = function ($tweets, tweet) {
var html = $.trim(template(tweet));
var $tweet = $(html);
$tweets.append($tweet);
$tweet.fadeIn();
if ($tweets.children().length > max_tweets) {
$tweets.children().first().remove();
}
};
var handler = function (e) {
var list = [];
var append = function (data) { list = list.concat(data.results); };
var promise1 = $.getJSON(url1).then(append);
var promise2 = $.getJSON(url2).then(append);
$.when(promise1, promise2).then(function () {
var d = new $.Deferred();
var d2 = d; // keep a duplicate to original deferred
$.each(list, function (i, tweet) {
// save promise returned by .then() to continue chaining
d2 = d2.then(sleep3).then(function () {adder($tweets, tweet); });
});
d.resolve(); // run the chain!
});
e.preventDefault();
};
$("#launch-ft").click(handler);
});