Edit in JSFiddle

// create a sub of jquery, basically, a copy we can mess with
var our$ = $.sub();

// make our$ have a modified animate function
our$.fn.animate = function( props, speed, easing, callback ) {
    // from jQuery.speed, forces arguments into props and options objects
    var options = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
            complete: callback || !callback && easing ||
                jQuery.isFunction( speed ) && speed,
            duration: speed,
            easing: callback && easing || easing && !jQuery.isFunction(easing) && easing
        };
    // create the deferred
    var dfd = our$.Deferred(),
        // a copy of the complete callback
        complete = options.complete,
        // and the count of how many items
        count = this.length;
    
    // make a new complete function
    options.complete = function() { 
        // that calls the old one if it exists
        complete && complete.call( this ); 
        // and decrements count and checks if it's 0
        if ( !--count ) {
            // and when it is, resolves the DFD
            dfd.resolve(); 
        }    
    };
    
    // all the hooks have been made
    jQuery.fn.animate.call( this, props, options );
        
    // return the promise that we'll do something
    return dfd.promise();
};


// retrieves content and updates a dom element
// returns a promise
function populateBox() {
    return $.ajax({
        url: $('input[name="fail"]').is(':checked') ? '/nowhere' : '/echo/html/',
        data: { delay: 3, html: "Thank you for viewing this box, isn't it wonderful?" },
        type: "POST",
        success: function( data ) {
            $('#content').slideDown().html( data );
        }
    });
}


$('button.load').click( function() {
    // save the button, box and loading as our$ objects
    var $button = our$(this).hide(),
        $box = our$('#box'),
        $loading = our$('.loading');
    // when the functions are done
    $.when( 
        // $box was created with our$, so it will
        // use the custom animate function
        $box.slideDown(),
        populateBox() 
    // then run the 1st function on success
    // and the second function if either fails
    ).then( 
        function() { 
            // remove loading, we're done
            $loading.slideUp();
        },
        function() {
            // get that button back here
            $button.show();
            // and hide the box
            $box.slideUp();  
        }
    ); 
});

<button class="load">Get Message</button> <input type="checkbox" name="fail" /> Fail Ajax
<div id="box">
    <div class="loading">Some kind of loading message</div>
    <div id="content"></div>
</div>
#box {
    -webkit-box-shadow: 0px 0px 7px #133475;
    -moz-box-shadow: 0px 0px 7px #133475;
    box-shadow: 0px 0px 7px #133475;
    -moz-border-radius: 5px;
    border-radius: 5px;
    display:none;
}
.loading {
    border:1px solid #000;
    padding:10px; 
    background:#888;
    -moz-border-radius: 5px;
    border-radius: 5px;
}
#content {
     padding:30px;  
     display:none; 
}