I'm making either 1 or more REST/ajax calls to validate some user information. The rest calls are working well and the information is coming back. The issue I'm facing isn't with that part of the code, which looks something like this.
function ensureUsers(recipients){
var promises = [];
for(var key in recipients){
var payload = {'property':recipients[key]};
promises.push( $.ajax({...}));
}
return $.when.apply($,promises);
}
....
ensureUsers(users) // users is an array of 1 or more users
.done(function(){
console.log(arguments);
)}
If there is more than one user in the initial array, then the arguments in my .done
code are structured like this:
[[Object,"success",Object],[Object,"success",Object]...]
I can then iterate over each result, check the status, and proceed.
However if there is only one user in the initial array then .done
gets arguments like this:
[Object,"success",Object]
It seems strange to me that the structure of what is returned would change like that. I couldn't find anything about this specific a problem, so I hacked together a solution
var promises = Array.prototype.slice.call(arguments);
if(!Array.isArray(promises[0])){
promises = [promises];
}
Is that really the best I can hope for? Or is there some better way to deal with the returned promises from 1 or more ajax calls in jQuery?
It seems strange to me that the structure of what is returned would change like that.
Yes, jQuery is horribly inconsistent here. When you pass a single argument to $.when
, it tries to cast it to a promise, when you pass multiple ones it suddenly tries to wait for all of them and combine their results. Now throw in that jQuery promises can resolve with multiple values (arguments), and add a special case for that.
So there are two solutions I could recommend:
Drop $.when
completely and just use Promise.all
instead of it:
var promises = [];
for (var p of recipients) {
…
promises.push( $.ajax({…}));
}
Promise.all(promises)
.then(function(results) {
console.log(results);
})
Make each promise resolve with only a single value (unlike $.ajax()
that resolves with 3) so that they don't get wrapped in an array, and $.when
will produce consistent results regardless of number of arguments:
var promises = [];
for (var p of recipients) {
…
promises.push( $.ajax({…}).then(function(data, textStatus, jqXHR) {
return data;
}) );
}
$.when.apply($, promises)
.then(function() {
console.log(arguments);
})
It appears this functionality is working as currently documented. When you have multiple deferreds passed to $.when
it creates a new Promise
object that is resolved with the results of each of the results of the passed in deferreds. When you only pass in one, it returns the deferred that you passed in, thus only returning the result instead of an array of results.
I'm not sure if it is any better than your current solution, but you could force it to always have multiple deferreds by having a "fake" deferred that you skip when evaluating the results.
I.E.
function ensureUsers(recipients){
var promises = [$.Deferred().resolve()];
for(var key in recipients){
var payload = {'property':recipients[key]};
promises.push( $.ajax({...}));
}
return $.when.apply($,promises);
}
You could also potentially make it so the placeholder deferred is resolved with the same structure as what you expect in your real results so it would just appear that the first response is always a success.