2012-08-16 72 views
3

jQuery具有Deferred API的好功能,$.wait()可与多个Deferred s/Promise s一起使用。它返回时:

  • 所有Deferred S的已经resolve() d

  • 其中Deferred S的一直reject()

大多数时候这是你想要的,但有时候你想知道什么时候全部是其中有reject() ed。

有没有简单或优雅的方式来做类似$.wait()但只有当所有Deferred s都被拒绝?

(可能还有其他的用例,但我的是这与waiting for the first of several Deferred's to resolve结合起来。)

+0

相关但不相同:?jQuery.when - 回调时,所有Deferreds不长“悬而未决”(解决或拒绝)( http://stackoverflow.com/questions/5824615/jquery-when-callback-for-when-all-deferreds-are-no-long-unresolved-either-r) – hippietrail 2012-08-16 12:23:14

+0

我提出了另一项针对jQuery的功能请求对'$ .when()'的增强:** [用于$ .WHEN()的选项参数来提供备选语义](http://bugs.jquery.com/ticket/12325)**。它也关闭了,但[jaubourg](https://github.com/jaubourg)增加了很好的分析和建议,可以帮助任何对这个问题感兴趣的人。 – hippietrail 2012-08-17 12:26:01

回答

2

在如何无极规范很可能对未来有PromiseInspection对象会精神,这里有一个jQuery的添加 - 功能上,告诉你,当所有的承诺都做了,是否履行或拒绝:

// 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); 
    }); 
} 

然后,您可以使用它像这样:

$.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 
     } 
    }); 
}); 

请记住,$.settle()将始终满足(永不拒绝),并且履行的值是一个PromiseInspection对象的数组,您可以询问每个对象以查看它是否被满足或拒绝,然后获取相应的值或原因。请参见下面的演示,例如使用:

工作演示:https://jsfiddle.net/jfriend00/y0gjs31r/

+0

似乎这个代码可以通过返回一个'Error(reason)'如果promise拒绝,并且如果它解析的值而不是处理'PromiseInspection'来改进/简化。然后,在消费者中,您可以测试'result instanceof Error'来查找最初被拒绝的承诺并获得拒绝原因。 – cjbarth 2017-06-01 18:55:34

+0

@cjbarth - 是的,那是另一种方式。有些人喜欢这种方式,也就是这样。两者都很好。曾经有一些标准讨论围绕使用类似'PromiseInspection'对象的方式来进行这些类型的使用,但我不知道这是否仍然是一个可能的方向 - 这就是为什么这样做的原因。我同意,按照你的建议来做这件事可能会更简单一些。这确实要求所有可能拒绝你使用的所有承诺的拒绝都使用'instanceof Error',这是合理的,但并不总是完成的。 – jfriend00 2017-06-02 21:49:23