On Github patrickberkeley / better-callbacks
It all starts with an innocent little ajax request:
  var data;
  $.get('api/data', function(resp) {
    data = resp.data;
  });
  doSomethingFancyWithData(data);
            
          
  // Demonstrates nesting, CPS, 'callback hell'
  //
  $.get('api1/data', function(resp1) {
    // Next that depended on the first response.
    $.get('api2/data', function(resp2) {
      // Next request that depended on the second response.
      $.get('api3/data', function(resp3) {
        // Next request that depended on the third response.
        $.get(); // ... you get the idea.
      });
    });
  });
            
          
  $.get('api1/data', function(resp1) { trackMe(); });
  $.get('api2/data', function(resp2) { trackMe(); });
  $.get('api3/data', function(resp3) { trackMe(); });
  var trackedCount = 0;
  function trackMe() {
    ++trackedCount;
    if (trackedCount === 3) {
      doSomethingThatNeededAllThree();
    }
  }
            
          
  // The deferred object itself.
  var def = $.Deferred;
            
            
  // And the deferred's promise object.
  def.promise();
            
          
  def;
  > { always: function() {},
      done: function() {},
      fail: function() {},
      notify: function() {},
      notifyWith: function() {},
      pipe: function() {},
      progress: function() {},
      promise: function() {},
      reject: function() {},
      rejectWith: function() {},
      resolve: function() {},
      resolveWith: function() {},
      state: function() {},
      then: function() {} }
            
          
  // A read-only version of the deferred object;
  // returned from a jQuery $.ajax() call.
  def.promise();
  > { always: function() {},
      done: function() {},
      fail: function() {},
      pipe: function() {},
      progress: function() {},
      promise: function() {},
      state: function() {},
      then: function() {} }
            
          
  var deferred = $.Deferred();
  deferred.promise().state();
  > "pending"
  deferred.resolve();
  deferred.promise().state();
  > "resolved"
            
          
  var deferred = $.Deferred();
  deferred.promise().state();
  > "pending"
  deferred.reject();
  deferred.promise().state();
  > "rejected"
            
          
  var deferred = $.Deferred();
  deferred.pipe(
    function(resp) {
      console.log("I filter done responses", resp);
    },
    function(resp) {
      console.log("I filter fail responses", resp);
    },
    function(resp) {
      console.log("I filter progress responses", resp);
    }
  );
            
          
  var deferred = $.Deferred();
  deferred.resolve();
  deferred.done(function() {
    console.log("I get called when things go well.");
  });
            
          
  var deferred = $.Deferred();
  deferred.reject();
  deferred.fail(function() {
    console.log("I get called when things fall apart.");
  });
            
          
  var deferred = $.Deferred();
  deferred.resolve();
  deferred.always(function() {
    console.log("I'm so popular, I get called no matter what.");
  });
            
          
  var deferred = $.Deferred();
  // Shorthand for done, fail, and always.
  deferred.then(
    function(resp) {
      console.log("I'm a done callback!", resp);
    },
    function(resp) {
      console.log("I'm a fail callback.", resp);
    },
    function(resp) {
      console.log("I'm an always callback.", resp);
    }
  );
            
          
  $.get('api1/data', function(resp1) {
    // Next that depended on the first response.
    $.get('api2/data', function(resp2) {
      // Next request that depended on the second response.
      $.get('api3/data', function(resp3) {
        // Next request that depended on the third response.
      });
    });
  });
            
          
  var req1 = $.get('api1/data');
  var req2 = $.get('api2/data');
  var req3 = $.get('api3/data');
  req1.then(function(req1Data) {
    return req2;
  }).then(function(req2Data) {
    return req3;
  });
  req2.then(function(req2Data) {
    // Do something somewhere entirely different.
  });
            
          
  $.get('api1/data', function(resp1) { trackMe(); });
  $.get('api2/data', function(resp2) { trackMe(); });
  $.get('api3/data', function(resp3) { trackMe(); });
  var trackedCount = 0;
  function trackMe() {
    ++trackedCount;
    if (trackedCount === 3) {
      doSomethingThatNeededAllThree();
    }
  }
            
          
  $.when(
    $.get('api1/data'),
    $.get('api2/data'),
    $.get('api3/data'),
    { key: 'value' }
  ).done();
            
          
  var getArticleByHeadline = function(query) {
    return $.ajax({ url: 'api/articles' })
      .pipe(function(data) {
        // Filter article headlines by query.
      });
  }
  getArticleByHeadline('wins election').done(function(articles) {
    // Do with the articles.
  });
            
          
  function showSpinner(){
    var deferred = $.Deferred();
    $('#spinner').fadeIn(1000, deferred.resolve());
    return deferred.promise();
  }
  function disableInputs(){ /* Disable form, return promise */ }
  function postData(){ return $.post('api/'); }
  $.when(showSpinner, disableInputs, postData, {random: 'obj'})
    .done(function() { /* Clear form */ })
    .fail(function() { /* Don't clear form, show error */ })
    .always(function() { /* Hide spinner, re-enable form */ });