How to chain jquery $.when

854 views Asked by At

I'm using

$.when.apply($, arrayOfDeferreds).then(function() {
  var args = Array.prototype.slice.call(arguments);
  var anotherArrayOfDeferreds = [];

  args.map(function(item){
      anotherArrayOfDeferreds.push(item.getSomething()); // item.getSomething() returns an deferred.
  });

  // return an anotherArrayOfDeferreds
});

to deal with an array of deferreds. However, in (do something here ...) above, it generates another array of deferreds, which essentially needs another $.when.apply() and which cannot be dealt with by .then(). I am wondering if there is a way to chain $.when?

Thanks!

3

There are 3 answers

2
Arun P Johny On

I think you can pass it again to $.when to create a new promise which can be returned from the then to make it chainable

$.when.apply($, array).then(function () {
    var newarray = [];

    return $.when.apply($, newarray)
});
8
jfriend00 On

$.when() does not generate another array of deferreds. It returns a single promise that will be resolved when all the promises you passed it were resolved (or rejected if any of those promises are rejected).

The results passed to the single promise's resolved handler are an array of results. In some cases (like with $.ajax(), it will be an array of arrays of results).

So, what you had:

$.when.apply($, array).then(function(arrayOfResults) {
    (do something here ...)
});

Will work just fine for telling you when all the promises you passed to $.when() have finised. The code in the .then() handler will be passed an array of results and will only be called when all the promises you passed to $.when() have been resolved.

If, inside of the .then() handler, you want to start up N more async operations, you can just use $.when() again inside there. Or, you each of your original async calls wants to have it's own chained operation, then you should chain those before passing them to $.when() so what is passed to $.when() is the result of chaining each individual chain.

For example, you could do this:

var p1 = $.ajax(...).then(...).then(...).then(...).then(...);
var p2 = $.ajax(...).then(...).then(...).then(...);

$.when(p1, p2).then(function(arrayOfResults) {
    // will be executed when all the chained operations in p1 and p2 are done
});

Or, you could do this for another series of parallel operations:

$.when.apply($, array).then(function(arrayOfResults) {
    // first up N more async operations
    return $.when.apply($, newArray).then(...);
});

If you want help beyond this, you will need to be much more specific about what your problem is. So far, it sounds like it could be handled by one of the circumstances above.


Based on your edited question, you could do this:

$.when.apply($, arrayOfDeferreds).then(function() {
  var args = Array.prototype.slice.call(arguments);

  var anotherArrayOfDeferreds = args.map(function(item){
      return item.getSomething(); // item.getSomething() returns an deferred.
  });
  $.when.apply($, anotherArrayOfDeferreds).then(function() {
      // called when this new array of deferreds is done
  });

});

But, this is a bit of an unusual structure. Usually, if there are more async operations to be performed after an async operation, you don't resolve and pass an item that can then be used on a later async operation, but rather you just chain the async operation right where you've got it.

So, rather than doing this inside your original async operation:

d.resolve(item);

which you must be using now, you'd do this:

return item.getSomething();

This would just chain the next async operation onto the previous one and your original $.when() would work for all.

0
Roamer-1888 On

Firstly, let's call them promises not deferreds, because there's no need ever to do this sort of thing with deferreds.

Now, assuming you don't need the promise returned by $.when.apply(arrayOfPromises) for any other purpose, then a double $.when() is unnecessary.

You can map arrayOfPromises to another array of promises and do a single $.when.apply() on that array.

$.when.apply($, arrayOfPromises.map(function(promise) {
    return promise.then(function(item) {
        return item.getSomething();
    });
})).then(function() {
    var resultsOfTheDualAsyncProcesses = Array.prototype.slice.call(arguments);
});

DEMO