15

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?

Bobby
  • 18,217
  • 15
  • 74
  • 89
  • possible duplicate of [Pass in an array of Deferreds to $.when()](http://stackoverflow.com/q/5627284/1048572) – Bergi Mar 30 '16 at 14:09

2 Answers2

35

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);
    });
};
josh3736
  • 139,160
  • 33
  • 216
  • 263
  • I think the OP is actually asking for a "shorter, cleaner" way and is already aware of `.apply()` – jAndy Aug 30 '12 at 23:20
  • I'll make the question clearer but I believe that this will call done with `result1,result2,...` but I'm looking to have it call done with single param of `[result1,result2,...]` see http://jsfiddle.net/qk2mT/2/ "throws Object 0 has no method 'join' " – Bobby Aug 30 '12 at 23:24
  • 1
    @Soldier.moth: The `done` handler does not get an array of results; rather, it gets passed *n* arguments, where *n* is the number of Deferreds you passed in originally. Use the implicit `arguments` object to loop through the results. – josh3736 Aug 30 '12 at 23:27
  • @josh3736: Yeah, I was hoping nice method to do the transformation of `arguments` to an array for me. Might be accomplished by defined `$.whenall` like `function(arr) { return $.when.apply($,arr).pipe(function(){ return [].slice.call(arguments,0); }); }` – Bobby Aug 30 '12 at 23:36
  • @Soldier.moth: Yep, that would do it. I'd use `Array.prototype.slice` instead of `[]` so you aren't unnecessarily creating an Array instance. See update. – josh3736 Aug 30 '12 at 23:54
6

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

jAndy
  • 231,737
  • 57
  • 305
  • 359
  • You have to be careful with `bind` since it isn't available in [older browsers](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind#Browser_compatibility). – josh3736 Aug 30 '12 at 23:19