Run multiple async functions with ‘deferred’ and ‘when’

How to use jQuery deferred to handle two or more asynchronous functions and then perform some action?

Let’s say we have a function that returns a value asynchronously after some time and we like to be able to call that function and use the returned value in some way. The most basic way solution for this is to use a callback object:

function someFunction(message, callback) {
 setTimeout(function () {
 callback(message);
 }, 2000);
}

Which works fine, but becomes harder to handle when we for example need to do something after several asynchronous functions has finished.

Use jQuery deferred
A better way is to do it like jQuery ajax, and return promises of deferred objects from our functions and resolve the objects when the functions are done.

It is much simpler to do than it might sound.

We need to
1) create a deferred object,
2) return it’s promise object, and
3) resolve it with a value when it’s done (or fail),

like this:

function someFunction(message) {
 var deferred = $.Deferred();
 // async wait 2 seconds:
 setTimeout(function () {
   deferred.resolve(message);
 }, 2000);
 return deferred.promise();
};

With that we call one function using the promise this way:

someFunction('hello').done(
  function (msg) {
     console.log(msg);
  });

Use jQuery.when to run multiple functions
To run multiple functions, and then perform some action after all are done, we can use jQuery.when:

$.when(someFunction('foo'), someFunction('bar'))
 .done(function (foo, bar) {
 console.log('return from first function: ' + foo);
 console.log('return from second function: ' + bar);
 });

If you don’t like this way of mapping all resolved items to the done function arguments you can instead use then on each deferred function, like in the following example.

A real example – ajax load templates and then perform some action when they are all done
Here’s what I use for loading ajax resources (jQuery ajax returns promises) and running a function when they are all done:

    $.when(

        $.get("/app/views/layout.html").then(function (data) {
            app.mainLayout = new kendo.Layout(data);
        }),

        $.get("/app/views/main.html").then(function (data) {
            app.mainView = new kendo.View(data, { model: mainViewModel });
        }),

        $.get("/app/views/about.html").then(function (data) {
            app.aboutView = new kendo.View(data);
        })

        ).done(function () {

            app.mainLayout.render($("#layoutContainer"));
            app.mainLayout.showIn("#Content", app.invoiceView);

            app.router = new kendo.Router();

            app.router.route("/", () => {
                app.mainLayout.showIn("#Content", app.mainView);
            });

            app.router.route("/about", () => {
                app.mainLayout.showIn("#Content", app.aboutView);
            });

            app.router.start();

        }).fail(function () {
            alert('Missing template file(s) ');
        });

Further reading in the jQuery docs.

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s