crishushu
crishushu

Reputation: 565

Generate from array sequential executions with deferreds (jquery)

Is there any mean to make this more elegant using other solutions (jquery is fine)?

Important in my case is that respective executions are only triggered if the previous ones have been terminated.

seqExe(["item one", "item two", "item three", "item four", "item five"])

function seqExe(corpus) {
    var i = -1,
        len = corpus.length,
        defer = jQuery.Deferred(),
        promise = defer.promise();

    while(++i < len) {
        promise = promise.then((function(item) {
            return function() {
                console.log(item);
                return foo(item);
            }
        }).call(this, corpus[i]));
    }

    promise.then(function() {
        console.log("Done");
    }, function() {
        console.error("Failed");
    });

    return defer.resolve();
}

function foo(item) {
    var defer = jQuery.Deferred();
    window.setTimeout(
        function() {
            defer.resolve()
            console.log(item);
        }, Math.random() * 2000 + 1000);

    return defer.promise();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

Supposed output should be:

item one
item one
item two
item two
item three
item three
item four
item four
item five
item five
Done

Upvotes: 1

Views: 82

Answers (1)

trincot
trincot

Reputation: 350272

This is an ideal candidate for using reduce. Furthermore your initial promise -- which you want to resolve immediately -- can be just $.when(), and will become the start value of the reduce call:

seqExe(["item one", "item two", "item three", "item four", "item five"])

function seqExe(corpus) {
    return corpus.reduce(function (p, item) {
        return p.then(function () {
            console.log(item + ' pending');
            return foo(item);
        });
    }, $.when())
    .then(function(){
        console.log("Done");
    }, function(){
        console.log("Failed");
    });
}

function foo(item) {
    var defer = new $.Deferred();
    window.setTimeout(
        function() {
            defer.resolve()
            console.log(item + ' resolved');
        }, Math.random() * 2000 + 1000);
    return defer.promise();
}
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>

Note that in some jQuery versions (I see this in 2.1.1) the then callbacks are executed synchronously, which would give the wrong output order. This is fixed in jQuery 3 (I tried 3.1.1, which I also linked to in the above snippet).

Here is how you would do it with native ES6 promises:

seqExe(["item one", "item two", "item three", "item four", "item five"])

function seqExe(corpus) {
    return corpus.reduce(function (p, item) {
        return p.then(function () {
            console.log(item + ' pending');
            return foo(item);
        });
    }, Promise.resolve())
    .then(function(){
        console.log("Done");
    }, function(){
        console.log("Failed");
    });
}

function foo(item) {
    return new Promise(function (resolve) {
        window.setTimeout(function() {
            resolve();
            console.debug(item + ' resolved');
        }, Math.random() * 2000 + 1000);
    });
}

Upvotes: 2

Related Questions