【问题标题】:Strange/unexpected jQuery promise reject behaviour奇怪/意外的 jQuery 承诺拒绝行为
【发布时间】:2016-10-06 03:18:05
【问题描述】:

我正在尝试一些奇怪的行为,使用 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/

我需要的是一种方法来获取两者的论点,拒绝和解决。

【问题讨论】:

标签: javascript jquery promise


【解决方案1】:

这是$.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()

【讨论】:

  • 那么在地狱里我没有办法得到所有承诺的论据吗?
  • 我花了一些时间来适应它,但似乎工作正常!非常感谢! :)
猜你喜欢
  • 1970-01-01
  • 2019-02-08
  • 2021-03-14
  • 1970-01-01
  • 2021-01-27
  • 2017-03-12
  • 1970-01-01
  • 2019-11-25
  • 2020-01-23
相关资源
最近更新 更多