jQuery has a nice feature of its Deferred
API, $.wait()
for working with multiple Deferred
s / Promise
s. It returns when:<
In the spirit of how the Promise specification is likely going for the future with a PromiseInspection
object, here's a jQuery add-on function that tells you when all promises are done, whether fulfilled or rejected:
// pass either multiple promises as separate arguments or an array of promises
$.settle = function(p1) {
var args;
if (Array.isArray(p1)) {
args = p1;
} else {
args = Array.prototype.slice.call(arguments);
}
function PromiseInspection(fulfilled, val) {
return {
isFulfilled: function() {
return fulfilled;
}, value: function() {
return fulfilled ? val: undefined;
}, reason: function() {
return !fulfilled ? val: undefined;
}
};
}
return $.when.apply($, args.map(function(p) {
// if incoming value, not a promise, then wrap it in a promise
if (!p || (!(typeof p === "object" || typeof p === "function")) || typeof p.then !== "function") {
p = $.Deferred().resolve(p);
}
// Now we know for sure that p is a promise
// Make sure that the returned promise here is always resolved with a PromiseInspection object, never rejected
return p.then(function(val) {
return new PromiseInspection(true, val);
}, function(reason) {
// convert rejected promise into resolved promise
// this is required in jQuery 1.x and 2.x (works in jQuery 3.x, but the extra .resolve() is not required in 3.x)
return $.Deferred().resolve(new PromiseInspection(false, reason));
});
})).then(function() {
// return an array of results which is just more convenient to work with
// than the separate arguments that $.when() would normally return
return Array.prototype.slice.call(arguments);
});
}
Then, you can use it like this:
$.settle(promiseArray).then(function(inspectionArray) {
inspectionArray.forEach(function(pi) {
if (pi.isFulfilled()) {
// pi.value() is the value of the fulfilled promise
} else {
// pi.reason() is the reason for the rejection
}
});
});
Keep in mind that $.settle()
will always fulfill (never reject) and the fulfilled value is an array of PromiseInspection
objects and you can interrogate each one to see if it was fulfilled or rejected and then fetch the corresponding value or reason. See the demo below for example usage:
Working demo: https://jsfiddle.net/jfriend00/y0gjs31r/
i had the same problem lately: I think that occurs because $.when
is more of a Promise.all
implementation.
I wasn't able to find a Promise.allSettled
alternative in the jQuery documentation and across the web so here's my attempt to get it:
const deferredsAllSettled = deferreds => {
const settlements = [];
deferreds.forEach(deferred => {
const settlement = jQuery.Deferred();
deferred.always(settlement.resolve);
settlements.push(settlement);
});
const returnedDeferred = $.Deferred();
jQuery.when
.apply(jQuery, settlements)
.then(() =>
returnedDeferred.resolve(
deferreds.map(deferred => deferred.promise())
)
);
return returnedDeferred.promise();
};
NOTE: I've written that for a utils file so the Promise.allSettled
one-only iterable parameter signature was ok for me, if you want to write it in the $.when
spirit (passing and getting the $.Deferred
s as separate arguments) you'll have to write a little more...