这是$.when() 的正常行为。如果您传递给$.when() 的任何承诺被拒绝,那么$.when() 将以其找到的第一个拒绝原因拒绝。这就是它的编码方式。
这类似于 ES6 Promise.all() 的工作方式。
如果您想要所有结果,即使某些 Promise 被拒绝,您也可以使用此代码中定义的 $.settle() 或 $.settleVal() 之类的内容:
(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);
});
}));
}
})();
$.settle() 总是会解析,它会解析 PromiseInspection 对象的数组,然后您可以遍历这些对象以查看哪些承诺已解决,哪些被拒绝,以及价值或原因是什么。
$.settleVal() 迭代起来更简单一些,但没有那么通用,因为它不会给你拒绝的理由。它总是用一个数组解析,其中一个拒绝将有一个默认值,你在数组中传递给它来代替解析的值。
仅供参考,$.settle() 和$.settleVal() 都可以传递一个像$.settle(arrayOfPromises) 这样的promise 数组,或者像$.settle(p1, p2, p3) 这样的多个promise 参数(就像$.when() 一样)。当您有一系列承诺时,这可以避免使用.apply()。