Binding jQuery promises in parallel using a simple alternative to $ .when.apply

When using jQuery promises sequentially, they can be chained using then

multiple times:

eg.

promise = promise.then(someoperation());

      

which also works inside a loop (very handy).

I have a similar scenario where I needed to know when multiple parallel operations were completed, but don't do the encoding overhead (like added complexity) of creating a promises array for the sole purpose of calling $.when.apply

After looking at my options, I came up with this pattern as an alternative:

promise = $.when(promise, anotherpromise);

      

To test this, I came up with this test:

var p = $.Deferred().resolve().promise();

[1,2,3,4,5,6,7,8,9,10].forEach(function(i){
     p = $.when(p, delay(i,i * 500));
});

p.then(function(){
   log("All done");
});

      

JSFiddle: http://jsfiddle.net/TrueBlueAussie/0rh8Lhv4/1/

Everything seems to be working fine, so I started applying it to another example on StackOverflow.

The next one I tried with this pattern is to fix the example from passing the $ .when () value to the lazy data array :

My code:

$("a").click(function () {
    var promise = GetSomeDeferredStuff();
    promise.then(function () {
        $("div").append("<p>All done!</p>");
    });
});

      

JSFiddle: http://jsfiddle.net/TrueBlueAussie/ts1dqwe3/1/

Q. For some reason, this never fires the last event. Can anyone spot the problem?

Update

Based on the comment from @Karl-André Gagnon

it seems that the initial one promise

can only be undefined and still work. Much simpler:

eg.

var p;
[1,2,3,4,5,6,7,8,9,10].forEach(function(i){
     p = $.when(p, delay(i,i * 500));
});
p.then(function(){
   log("All done");
});

      

+3


source to share


2 answers


Ok, it turns out that this pattern actually works pretty well, but you need to make sure that the initial promise

chaining you're attached to is already resolved:

function GetSomeDeferredStuff() {
    var promise = $.Deferred().resolve().promise();

      

JSFiddle: http://jsfiddle.ne/TrueBlueAussie/ts1dqwe3/2/

Thus, this pattern is really a simple alternative to creating a use-only array $.when.apply

.



Also, as @ Karl-André Gagnon pointed out, if you start with undefined, it does the same. Better:)

function GetSomeDeferredStuff() {
    var promise;

      

JSFiddle: http://jsfiddle.net/TrueBlueAussie/ts1dqwe3/4/

+2


source


Update

I have a similar scenario where I need to know when multiple concurrent operations have completed but the overhead of creating an array of promises has not been met for the sole purpose of calling

loop

not used; only $.when()

; the same pattern used when implementing via $.map()

. Also added "random" delay

to jsfiddle ajax request

; promise.then()

should not be called up until will not be completed all the challenges of parallel functions $.when()

, next

, queueName

.

function GetSomeDeferredStuff(elem, name, cycles) {
    var el = (elem || {}),
        queueName = (name || "q"),
        len = (cycles || 5),
        request = function () {
            return $.ajax({
                type: "POST",
                url: '/echo/html/',
                data: {
                    html: "<p>Task #" + $.now() + " complete.",
                    delay: Math.random() * 5
                },
                success: function (data) {
                    $("div").append(data);
                }
            })
        }
    return $(el).queue(queueName, function (next) {
        return $.when(request()
                      , request()
                      , request()
                      , request()
                      , request())
                      .then(next)
    }).dequeue(queueName).promise(queueName);
}

$(function () {
    $("a").click(function () {
        var promise = GetSomeDeferredStuff();
        promise.then(function () {
            $("div").append("<p>All done!</p>");
        });
    });
});

      

jsfiddle http://jsfiddle.net/ts1dqwe3/10/




When using jQuery promises sequentially, they can be chained using then multiple times ... which also works inside a loop (very convenient).

Try to use .queue()

.promise(queueName)

function GetSomeDeferredStuff(elem, name, cycles) {
    // if no `elem`:`this` object passed , utilize empty object `{}`
    var el = (elem || {})
          // if no `name` passsed, utilize `String` `"q"`
        , queueName = (name || "q")
          // if no `cycles` how many functions passed to `.queue(queueName)`,
          // pass `5` functions to `.queue(queueName)`
        , len = (cycles || 5);
    return $(el).queue(queueName
    , $.map(Array(len), function (_, index) {
        return function (next) {
            return $.ajax({
                type: "POST",
                url: '/echo/html/',
                data: {
                    html: "<p>Task #" + (1 + index) + " complete.",
                    delay: (index + 1) / 2
                },
                success: function (data) {
                    return $("div").append(data);
                }
            // call "next" function in `queue`
            }).then(next)
        }
       // `.dequeue(queueName)` , return `queueName` jQuery promise object,
       // when all functions in `queue(queueName)` called ; 
       // `.queue(queueName)` empty
    })).dequeue(queueName).promise(queueName);
}

$(function () {
    $("a").click(function () {
        var promise = GetSomeDeferredStuff();
        promise.then(function () {
            // `this`:`elem` , or `{}`
            $("div").append("<p>All done!</p>");
        });
    });
});

      

jsfiddle http://jsfiddle.net/ts1dqwe3/6/

0


source







All Articles