Here's an contrived example of what's going on: http://jsfiddle.net/adamjford/YNGcm/20/
HTML:
<a href="#">Click me!</a>
<div></div>
JavaScript:
function getSomeDeferredStuff() {
var deferreds = [];
var i = 1;
for (i = 1; i <= 10; i++) {
var count = i;
deferreds.push(
$.post('/echo/html/', {
html: "<p>Task #" + count + " complete.",
delay: count
}).success(function(data) {
$("div").append(data);
}));
}
return deferreds;
}
$(function() {
$("a").click(function() {
var deferreds = getSomeDeferredStuff();
$.when(deferreds).done(function() {
$("div").append("<p>All done!</p>");
});
});
});
I want "All done!" to appear after all of the deferred tasks have completed, but $.when()
doesn't appear to know how to handle an array of Deferred objects. "All done!" is happening first because the array is not a Deferred object, so jQuery goes ahead and assumes it's just done.
I know one could pass the objects into the function like $.when(deferred1, deferred2, ..., deferredX)
but it's unknown how many Deferred objects there will be at execution in the actual problem I'm trying to solve.
If you're transpiling and have access to ES6, you can use spread syntax which specifically applies each iterable item of an object as a discrete argument, just the way
$.when()
needs it.MDN Link - Spread Syntax
I want to propose other one with using $.each:
We may to declare ajax function like:
Part of code where we creating array of functions with ajax to send:
And calling functions with sending ajax:
I had a case very similar where I was posting in an each loop and then setting the html markup in some fields from numbers received from the ajax. I then needed to do a sum of the (now-updated) values of these fields and place in a total field.
Thus the problem was that I was trying to do a sum on all of the numbers but no data had arrived back yet from the async ajax calls. I needed to complete this functionality in a few functions to be able to reuse the code. My outer function awaits the data before I then go and do some stuff with the fully updated DOM.
The workarounds above (thanks!) don't properly address the problem of getting back the objects provided to the deferred's
resolve()
method because jQuery calls thedone()
andfail()
callbacks with individual parameters, not an array. That means we have to use thearguments
pseudo-array to get all the resolved/rejected objects returned by the array of deferreds, which is ugly:Since we passed in an array of deferreds, it would be nice to get back an array of results. It would also be nice to get back an actual array instead of a pseudo-array so we can use methods like
Array.sort()
.Here is a solution inspired by when.js's
when.all()
method that addresses these problems:Now you can simply pass in an array of deferreds/promises and get back an array of resolved/rejected objects in your callback, like so:
If you're using angularJS or some variant of the Q promise library, then you have a
.all()
method that solves this exact problem.see the full API:
https://github.com/kriskowal/q/wiki/API-Reference#promiseall
https://docs.angularjs.org/api/ng/service/$q
To pass an array of values to any function that normally expects them to be separate parameters, use
Function.prototype.apply
, so in this case you need:See http://jsfiddle.net/YNGcm/21/
In ES6, you can use the
...
spread operator instead:In either case, since it's unlikely that you'll known in advance how many formal parameters the
.then
handler will require, that handler would need to process thearguments
array in order to retrieve the result of each promise.