【问题标题】:Why master deferred in not being rejected early in $.when as soon as one of the Deferreds is rejected?为什么 master deferred 在 $.when 中的一个被拒绝时不会在早期被拒绝?
【发布时间】:2014-05-30 09:56:42
【问题描述】:

根据http://api.jquery.com/jquery.when/ $.when() 应该在其中一个 Deferred 被拒绝后立即拒绝 master Deferred。为什么在“捕获 OnPreRenderFailed”消息之前,我会收到所有 4 条 console.log 消息。
我的理解是,一旦第二次延期被拒绝,我就不应该得到console.log('third deferred');console.log('fourth deferred'); 或者在console.log('caught OnPreRenderFailed');之后得到它们
除非我非常幸运(或没有)让方法一直以 (1,3,4,2) 顺序触发。我一定在这里遗漏了什么。
JSFiddle

 $(document).on('OnPreRenderResolved', function () {
       console.log('caught OnPreRenderResolved');
   });
   $(document).on('OnPreRenderFailed', function () {
       console.log('caught OnPreRenderFailed');
   })
    $(document).on('OnPreRender', function (e, options) {
       console.log('OnPreRender fired');
       options.callback();
   });
   $(document).trigger('OnPreRender', {
       callback: function () {
           $.when.apply($, [$.Deferred(function (deferred) {
               console.log('first deferred');
               deferred.resolve();
           }), $.Deferred(function (deferred) {
               console.log('second deferred');
               deferred.reject();
           }), $.Deferred(function (deferred) {
               console.log('third deferred');
               deferred.resolve();
           }), $.Deferred(function (deferred) {
               console.log('fourth deferred');
               deferred.resolve();
           })]).then(function () {
               $(document).trigger("OnPreRenderResolved");
           }, function () {
               $(document).trigger("OnPreRenderFailed");
           })
       }
   });

【问题讨论】:

  • 您将获得 4 个日志,因为您并行触发了 4 个函数。就那么简单。现在的问题是:你想达到什么目的?通常$.when 中的工作应该是独立的。你的意思是他们应该取决于其他人是否成功。如果是这样,那么您不应该使用$.when,而是将它们链接起来。

标签: javascript jquery jquery-deferred


【解决方案1】:

问题是评估总是同步的。你不能在两者之间强加任何东西。所以为了做到这一点,你必须强制你的函数异步。您可以使用setTimeout(..., 0); 来实现这一点:

$.when.apply($, [
    $.Deferred(function (deferred) {
        setTimeout(function() {
            console.log('first deferred');
            deferred.resolve();
        }, 0);
    }),
    $.Deferred(function (deferred) {
        setTimeout(function() {
            console.log('second deferred');
            deferred.resolve();
        }, 0);
    }),
    ...
]);

永远不要在同步代码中使用$.when。那简直毫无意义。

但是请注意,setTimeout 的顺序没有定义。你可能会得到奇怪的结果。

【讨论】:

  • jsfiddle.net/e7k8T/4 显示 console.log('caught OnPreRenderFailed')console.log('third deferred');console.log('fourth deferred'); 之前被解雇,这让我大开眼界。
【解决方案2】:

发生这种情况仅仅是因为 JS 解释器必须先评估传递给 $.when 的参数,然后才能实际调用 $.when

由于对$.Deferred 的调用都是内联参数,因此它们都将完成(输出它们的console.log 消息),但只有在它们全部完成后$.when 才会继续,此时它将确定其中一个承诺被拒绝了。

好像你写过:

f(a(), b(), c(), d());

每个a ... d 必须在f 被调用之前返回。

此外,由于您的函数都没有启动任何异步操作,因此这些函数的控制台输出绝对不可能出现在 调用 $.when 之后。

【讨论】:

  • 你能想出重组代码的方法,以便b()(或任何)被拒绝会终止进程吗?
  • @LIUFA 当然 - 只是不要将它们内联。首先创建了四个延迟对象,然后将它们的结果传递给$.when。不过,您仍然会看到日志消息。或者你是说你不想要例如如果第二个失败,第三个进程甚至启动?
  • 这不是真的。首先:你不能停止这些过程。同样来自文档:“该方法将在所有 Deferred 解决后立即解决其主 Deferred,或者在其中一个 Deferred 被拒绝后立即拒绝主 Deferred。”所以它不会等到所有这些都完成(除非代码是同步的,在这种情况下)。
  • @freakish 我想你误解了。 $.when 在评估其参数之前甚至无法开始考虑要做什么。这就是函数参数的工作原理。
  • @LIUFA 如果您只想要正常行为,这就是我的意思。如果您不希望 c 在解决 b 之前启动,则必须将它们串联起来。
【解决方案3】:

不完全是,当仍然被拒绝但其他异步进程没有停止时,什么时候会在第一个无法解决时立即报告拒绝。我见过的唯一能够取消承诺的图书馆是 bluebird

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2022-07-15
    • 2013-07-16
    • 2021-01-01
    • 2017-08-08
    • 1970-01-01
    • 2017-01-16
    相关资源
    最近更新 更多