$.when()
имеет «неудачный быстрый» дизайн. Это означает, что первое обещание, которое терпит неудачу, вызывает отказ $.when()
, и вы получаете информацию об отказе. С JQuery документ:
The method will resolve its master Deferred as soon as all the Deferreds resolve, or reject the master Deferred as soon as one of the Deferreds is rejected
Вы можете, однако, использовать другой тип функции, чтобы контролировать вашу группу обещаний. Этот тип функций часто называют «улаживать», когда вы ждете, пока все обещания не будут урегулированы, и вы получите результаты от всех из них независимо от того, решили ли они или отклонили.
Вот такая реализация для JQuery promies, что я использовал в прошлом, которые вы можете использовать, как это:
$.settle([ajax1Succes(), ajax2Success(), ajax3BoundtoFail()]).then(function(results) {
// results is an array of PromiseInspection Objects
// for each of them, you can see if the corresponding promise
// succeeded with a value or failed with an error
results.forEach(function(pi, index) {
if (pi.isFulfilled()) {
console.log("Promise #" + (index + 1) + " succeeded with result " + pi.value());
} else {
console.log("Promise #" + (index + 1) + " failed with reason " + pi.reason());
}
});
});
Или, немного проще версии использовать, если вам не нужно точное сообщение об ошибке:
$.settleVal(null, [ajax1Succes(), ajax2Success(), ajax3BoundtoFail()]).then(function(results) {
// results contains the results from all the successful promises
// any promises that has an error will show null as the result
});
Обратите внимание, они используют интерфейс больше похож на стандартный Promise.all()
, где они принимают массив обещаний и разрешения на массив результатов, так как это, как правило, как правило, легче использовать в реальном мире.
И вот реализация:
(function() {
function isPromise(p) {
return p && (typeof p === "object" || typeof p === "function") && typeof p.then === "function";
}
function wrapInPromise(p) {
if (!isPromise(p)) {
p = $.Deferred().resolve(p);
}
return p;
}
function PromiseInspection(fulfilled, val) {
return {
isFulfilled: function() {
return fulfilled;
}, isRejected: function() {
return !fulfilled;
}, isPending: function() {
// PromiseInspection objects created here are never pending
return false;
}, value: function() {
if (!fulfilled) {
throw new Error("Can't call .value() on a promise that is not fulfilled");
}
return val;
}, reason: function() {
if (fulfilled) {
throw new Error("Can't call .reason() on a promise that is fulfilled");
}
return val;
}
};
}
// 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);
}
return $.when.apply($, args.map(function(p) {
// make sure p is a promise (it could be just a value)
p = wrapInPromise(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 by returning a resolved promised
// One could just return the promiseInspection object directly if jQuery was
// Promise spec compliant, but jQuery 1.x and 2.x are not so we have to take this extra step
return wrapInPromise(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);
});
}
// simpler version that just converts any failed promises
// to a resolved value of what is passed in, so the caller can just skip
// any of those values in the returned values array
// Typically, the caller would pass in null or 0 or an empty object
$.settleVal = function(errorVal, p1) {
var args;
if (Array.isArray(p1)) {
args = p1;
} else {
args = Array.prototype.slice.call(arguments, 1);
}
return $.when.apply($, args.map(function(p) {
p = wrapInPromise(p);
return p.then(null, function(err) {
return wrapInPromise(errorVal);
});
}));
}
})();