jQuery promises

Hi,

I’ll post in English this time in order to have a larger audience, hopefully. So let’s begin…

These last weeks I have been writing asynchronous javascript code for a full javascript application. As the application grows so does code complexity, then  in order to keep your code clean and easy to maintain, you will somehow have to refactor it or else you’ll have to deal with « callback spaghetti » code. At that point, a good knowledge of jQuery promises and the following tricks might be usefull.

Let’s say you have a random number of promises you want to synchronise : like getting all the results before to do something else. Thanks to Herman Radtke, you can do something like :

    function par(array) {
        var deferred = $.Deferred();
        var fulfilled = 0, length = array.length;
        var results = [];

        if (array && array.length == 0) {
            deferred.resolve(results);
        } else {
            array.forEach(function(promise, i) {
                $.when(promise()).then(function(value) {
                    results[i] = value;
                    fulfilled++;
                    if (fulfilled === length) {
                        deferred.resolve(results);
                    }
                });
            });
        }
        return deferred.promise();
    }

Given this kind of input, here is how to use it :

    var promises = function() {
        var promises = [];
        for (var i = 0; i < 5; i++) {
            promises.push(function() {
                return $.Deferred(
                        function(dfd) {
                            // Whatever task
                            dfd.resolve("my result");
                        }).promise();
            });
        }
        return promises;
    };

    $.when(par(proms)).then(function(results) {
        assert(results.length == proms.length, "Ok");
    });

Now in another use case, you want the promises to be executed in sequence and get the results in correct order. So in the same idea we can write :

    function seq(array) {
        var seed = $.Deferred(), result = $.Deferred(), finalPromise;
        var results = [];

        finalPromise = array.reduce(function(promise, request) {
            return promise.pipe(function(input) {
                if (input) results.push(input);
                return request();
            });
        }, seed.promise());
        seed.resolve();

        finalPromise.done(
                function(i) {
                    results.push(i);
                }).then(function() {
                    result.resolve(results);
                });
        return result.promise();
    }

Thanks to my mate Sadek Drobi, here is a much better version of the two previous functions, in a very elegant functional style:

    function seq(array) {
        var seed = $.Deferred().resolve([]);
        if (!array || array.length == 0) return seed;
        else {
            return array.reduce(function(state, promise) {
                return state.pipe(function(input) {
                    return promise().pipe(function(i) {
                        return input.concat(i);
                    });
                });
            }, seed.promise());
        }
    }

    function par(array) {
        var seed = $.Deferred().resolve([]);
        if (!array || array.length == 0) return seed;
        else {
            return array.map(
                    function(p) {
                        return p();
                    }).reduce(function(state, promise) {
                        return state.pipe(function(input) {
                            return promise.pipe(function(i) {
                                return input.concat(i);
                            });
                        });
                    }, seed.promise());
        }
    }

In order to test that everything gets executed in the right order, I have written a very little node.js server script that waits a few seconds before responding to a request. You can find the whole code on github.

Constructive comments are highly appreciated.

Cheers.

Advertisements

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :