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?
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);
});
};
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