奇怪/意外的jQuery承诺拒绝行为

时间:2016-06-06 13:26:26

标签: javascript jquery promise

我正在试验一些奇怪的行为与拒绝的jQuery承诺。 我有一系列的承诺,当他们全部被解决/拒绝时,我需要与他们合作。

为此,我正在使用它:

var array_res = [];
array_res.push(promiseResolve('a'));
array_res.push(promiseReject('b'));

$.when.apply(null,array_res).always( function ( ) {
  console.log(arguments);
  //Work to do
} );

function promiseResolve (c) {
  var promise = $.Deferred();
  promise.resolve({a:c});
  return promise;
}

function promiseReject (c) {
  var promise = $.Deferred();
  promise.reject({b:c});
  return promise;
}

问题是:

如果我同意这两个承诺,一切正常。

如果我拒绝其中一项承诺,那么争论就不完整了。

如果我拒绝他们两个,那么争论就不完整了。

以下是3个小提琴,您可以在其中查看行为:

https://jsfiddle.net/daepqzv1/1/

https://jsfiddle.net/daepqzv1/2/

https://jsfiddle.net/daepqzv1/3/

我需要的是获取两者的论据,拒绝和解决的方法。

1 个答案:

答案 0 :(得分:2)

这是$.when()的正常行为。如果您传递给$.when()的任何承诺拒绝,则$.when()将拒绝其找到的第一个拒绝原因。这是它的编码方式。

这类似于ES6 Promise.all()的工作方式。

如果您想要所有结果,即使某些承诺拒绝,那么您可以使用此代码中定义的$.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)或多个承诺参数$.settle(p1, p2, p3)(因为$.when()有效) 。当你拥有一系列承诺时,这可以节省使用.apply()