Running Asnychronous Functions in Sequence

This demo shows two ways to run asynchronous functions that use the jQuery Deferred API (like $.ajax or $.get requests) in sequence

by Patrick Hund

HTML

<div class="container">
    <div class="row">
        <div class="col-md-12">
             <h2>Running Asnychronous Functions in Sequence</h2>

             <h4>
                        This demo shows two ways to run asynchronous functions that use the
                        <a href="http://api.jquery.com/category/deferred-object/">jQuery Deferred API</a>
                        (like <a href="http://api.jquery.com/jQuery.ajax/">$.ajax</a> or
                        <a href="http://api.jquery.com/jQuery.get/">$.get</a> requests) in sequence:
                    </h4>

        </div>
    </div>
    <div class="row">
        <div class="col-md-6">
            <p>The left button runs the functions by nesting the returned deferred objects with their <em>then</em> methods, like so:</p> <pre>
one().then(function () {
    two().then(function () {
        three().then(function () {
            four();
        });
    });
});
                    </pre>

        </div>
        <div class="col-md-6">
            <p>The right button runs the functions by using a helper function that executes them using recursion, like so:</p> <pre>
function invoke() {
    var functions = arguments;
    (function recursion(counter, deferred) {
        if (counter === functions.length) {
            return;
        }
        if (deferred) {
            deferred.then(function () {
                recursion(counter + 1, functions[counter]());
            });
        } else {
            recursion(counter + 1, functions[counter]());
        }
    }(0));
}
invoke(one, two, three, four);
                    </pre>

        </div>
    </div>
    <div class="row">
        <div class="col-md-6">
            <button id="nestedThens" type="button" class="btn btn-primary full-width">run with nested "then" methods</button>
        </div>
        <div class="col-md-6">
            <button id="recursion" type="button" class="btn btn-primary full-width">run with recursion function</button>
        </div>
 ...

CSS

.console {
    background-color: #000000;
    width: 100%;
    height: 200px;
    overflow: scroll;
    padding: 10px;
    border-radius: 4px;
}
.console p {
    margin: 0;
    color: green;
}
.console p.new {
    color: lime;
}
.margin-top {
    margin-top: 10px;
}
.margin-bottom {
    margin-bottom: 10px;
}
.full-width {
    width: 100%;
}

JavaScript

var one, two, three, four;

    function log(msg) {
        var $console = $("#console");
        $console.find("p").last().removeClass("new");
        $console.append("<p class=\"new\">" + msg + "</p>");
        $console.scrollTop($console.prop("scrollHeight"));
    }

    function withDelay(func) {
        var def = $.Deferred();
        window.setTimeout(function () {
            func();
            def.resolve();
        }, 1000);
        return def;
    }

    function logWithDelayF(msg) {
        return function () {
            return withDelay(function () {
                log(msg);
            });
        };
    }

    function invoke() {
        var functions = arguments;
        (function recursion(counter, deferred) {
            if (counter === functions.length) {
                return;
            }
            if (deferred) {
                deferred.then(function () {
                    recursion(counter + 1, functions[counter]());
                });
            } else {
                recursion(counter + 1, functions[counter]());
            }
        }(0));
    }

    one = logWithDelayF("function " + 1);
    two = logWithDelayF("function " + 2);
    three = logWithDelayF("function " + 3);
    four = logWithDelayF("function " + 4);

    function withNestedThens() {
        log("running with nested thens...");
        one().then(function () {
            two().then(function () {
                three().then(function () {
                    four();
                });
            });
        });
    }

    function withRecursion() {
        log("running with recursion function...");
        invoke(one, two, three, four);
    }

    $("#nestedThens").click(withNestedThens);
    $("#recursion").click(withRecursion);