Deferreds

How to use deferreds to attach an unknown number of possibly asynchronous functions to a save button.

by nate

HTML

<p>This is a save button. There is an unknown number of things on the page that need to run functions on click but before the final save event.</p>

<button>Save</button>

<div></div>

CSS

body {
    color: #666;
    font-family: Helvetica, sans-serif;
    padding: 25px;
}

p {
    margin-bottom: 1em;
}

button {
    margin-bottom: 1em;
}

p:last-child {
    color: red;
}

JavaScript

// This is a first-draft model for a hooks pattern for saving
// that accomodates both synchronous and asynchronous functions

// Questions:
// Does this way of adding functions to the hooks array make sense?
// Is there a way we could make it simpler?

var $div = $('div'),
    hooks = [];

/* Run all the functions in the hooks array, then save */
$('button').on('click', function (event) {

    $.when.apply($, $.map(hooks, function (hook) { return hook(); })).done(function () {
        $div.append('<p>All hooked functions have finished.\nNOW we can save.</p>');
    });

});

// Add an AJAX call to the hooks
// AJAX calls have built-in support for deffereds
hooks.push(function () {
    return $.post('/echo/html/', {
        html: "<p>AJAX task #1 complete.</p>",
        delay: 1
    }).success(function(data) {
        $("div").append(data);
    });
});

// Add an AJAX call to the hooks
hooks.push(function () {
    return $.post('/echo/html/', {
        html: "<p>AJAX task #2 complete.</p>",
        delay: 2
    }).success(function(data) {
        $("div").append(data);
    });
});

// Add an AJAX call to the hooks
hooks.push(function () {
    return $.post('/echo/html/', {
        html: "<p>AJAX task #3 complete.</p>",
        delay: 3
    }).success(function(data) {
        $("div").append(data);
    });
});

// Add an AJAX call to the hooks
hooks.push(function () {
    return $.post('/echo/html/', {
        html: "<p>AJAX task #4 complete.</p>",
        delay: 4
    }).success(function(data) {
        $("div").append(data);
    });
});

// Add a regular ol' function to the hooks
hooks.push(function () {
    $div.append('<p>I\'m just a regular function. I am not asynchronous.</p>');
});

// Add a non-AJAX function that might take some time...
// it uses a custom deferred
hooks.push(function () {
    var dfd = $.Deferred();
    
    $div.append('<p>I\'m a custom function that needs a deferred. It might take six seconds.</p>');
    
    setTimeout(function () {
   ...