What is cleanest way to turn Array of JQuery Promises into a JQuery Promise of an Array?

I run into the situation where I have multiple JQuery Promises in an Array

var arrayOfPromises = [ $.Deferred(), $.Deferred(), $.Deferred(), $.Deferred() ]

and need to turn it into a JQuery Promise of an Array

var promiseOfArray = someTransform(arrayOfPromises)

where

promiseOfArray.done(function(anArray){
  alert(anArray.join(","));
});

creates an alert with text

result1,result2,result3,result4

I currently define someTransform in coffeescript as

someTransform = (arrayOfPromises) ->
  $.when(arrayOfPromises...).pipe (promises...) ->
    promises

which transforms to the following javascript

var someTransform,
  __slice = [].slice;

someTransform = function(arrayOfPromises) {
  return $.when.apply($, arrayOfPromises).pipe(function() {
    var promises;
    promises = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
    return promises;
  });
};

Here is a jsFiddle of the result I'm looking for.

I was wondering if there is a better(shorter,cleaner) way to define someTransform to achieve the same result?

Answers:

Answer

You can just apply the array as the arguments to $.when.

var promiseOfArray = $.when.apply($, arrayOfPromises);

To make the usage of this clearer, I like adding a method to $:

$.whenall = function(arr) { return $.when.apply($, arr); };

Now you can do:

$.whenall([deferred1, deferred2, ...]).done(...);

Update: By default, the done handler gets each result passed as a separate argument; you don't get an array of results.

Since you need to handle an arbitrary number of Deferreds, you can use the special implicit arguments object to loop over the results.

$.whenall([d1, d2, ...]).done(function() {
    for (var i = 0; i < arguments.length; i++) {
        // do something with arguments[i]
    }
});

If you really just want to join the string result of all your Deferreds, we can employ a little array hackery. arguments is array-like, but is not an Array:

$.whenall([d1, d2, ...]).done(function() {
    alert(Array.prototype.join.call(arguments, ','));
});

If you want to return an array of results to your done callback, we can tweak whenall to do it:

$.whenall = function(arr) {
    return $.when.apply($, arr).pipe(function() {
        return Array.prototype.slice.call(arguments);
    });
};
Answer

It also bothered me alot to always type the "ugly" line $.when.apply when we need to invoke it on multiple promises. But Function.prototype.bind for the rescue!

var when = Function.prototype.apply.bind( jQuery.when, null );

Now, we can just call

when( someArrayWithPromises ).done(function() {
});

Function.prototype.bind is part of ES5 and is very widely available across browsers. There are a ton of easy shims available if you need to support very old'ish browsers aswell

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.