【问题标题】:jQuery Deferred's, $.when() and the fail() callback argumentsjQuery Deferred、$.when() 和 fail() 回调参数
【发布时间】:2011-07-27 22:52:11
【问题描述】:

我在使用$.when() 时得到一个意外结果,而其中一个延迟操作没有成功。

以这个 JavaScript 为例,它创建了 2 个延迟。第一个成功,第二个失败。

var f1 = function() {
    return $.Deferred(function(dfd) {
        dfd.resolve('123 from f1');
    }).promise();
};

var f2 = function() {
    return $.Deferred(function(dfd) {
        dfd.reject('456 from f2');
    }).promise();
};

$.when(f1(), f2())
    .then(function(f1Val, f2Val) {
        alert('success! f1, f2: ' + JSON.stringify([f1Val, f2Val]));
    })
    .fail(function(f1Val, f2Val) {
        alert('fail!    f1, f2: ' + JSON.stringify([f1Val, f2Val]));
    });

自己运行:http://jsfiddle.net/r2d3j/2/

我收到fail! f1, f2: ["456 from f2", null]

问题是在.fail() 回调中,通过f2() 拒绝传递的值被路由到第一个参数,我希望是f1Value。这意味着我真的没有办法知道哪个延迟对象实际发布了reject(),我也不知道失败数据实际上属于哪个操作。

我原以为.fail() 会得到参数null, '456 from f2',因为第一个 deferred 没有失败。还是我只是在这里没有正确执行延期?

如果不遵守回调中的参数顺序,我如何知道哪些延迟失败,以及哪些拒绝参数属于哪个失败的延迟?

【问题讨论】:

    标签: javascript jquery jquery-deferred


    【解决方案1】:

    $.when() 将在任何一个参数失败时立即执行失败的回调(传递给then() 的第二个参数)。这是设计使然。引用文档:

    http://api.jquery.com/jQuery.when/

    在其中一个 Deferred 被拒绝的多个 Deferred 情况下,jQuery.when 立即为其主 Deferred 触发 failCallbacks。请注意,此时某些 Deferreds 可能仍未解决。如果您需要针对这种情况执行额外的处理,例如取消任何未完成的 ajax 请求,您可以在闭包中保留对底层 jqXHR 对象的引用,并在 failCallback 中检查/取消它们。

    实际上没有内置的方法来获取回调,该回调会等待所有回调完成,而不管它们的成功/失败状态如何。

    所以,我为你建立了一个$.whenAll() :)
    它总是等到它们都以一种或另一种方式解决:

    http://jsfiddle.net/InfinitiesLoop/yQsYK/51/

    $.whenAll(a, b, c)
        .then( callbackUponAllResolvedOrRejected );
    

    【讨论】:

    • 您说“将立即执行 then() 回调”,但您引用的引用说“立即触发 failCallbacks” - 那是什么? \-:
    • @hippietrail 两者兼而有之。 then() 在 deferred 被“解决”或“拒绝”时触发。这归类为拒绝,因此会触发。
    • 不,它不会触发 then 回调 .. 它会触发 fail 回调。 (从技术上讲,它可以是传递给 'then' 的 'second' 回调,但无论如何它与大多数阅读您的答案的人认为是 'success' 回调的 'then' 回调不同)。
    • 这个答案在将近 5 年后仍然很有帮助!谢谢!
    • @InfinitiesLoop 我们这里有更新吗?还是仍然被认为是作为解决方案提供的最佳答案?
    【解决方案2】:

    在内部,“reject”和“fail”路径由两个完全独立的队列处理,所以它不能按您期望的方式工作。

    为了知道“when()”组中哪个原始 Deferred 失败,您可以让它们与“.reject()”调用一起作为对象字面量或其他内容的一部分传递。

    【讨论】:

    • 嗯,不是我设计的那样,但我想这让我的结果有意义。
    • @Mootoo 自从回答了这个问题以来,jQuery 发生了很大的变化。它现在使用标准的 Promise 语义。
    【解决方案3】:

    我也遇到过同样的问题,我通过使用 .always 回调并检查我的延迟对象数组来解决它。我有未知数量的 ajax 调用,所以我必须执行以下操作:

    // array of ajax deletes
    var deletes = [];
    $checkboxes.each(function () {
        deletes.push(deleteFile(this));
    });
    
    $.when.apply($, deletes)
      .always(function () {
          // unfortunately .fail shortcircuits and returns the first fail,
          // so we have to loop the deferred objects and see what happened.
    
          $.each(deletes, function () {
              this.done(function () {
                  console.log("done");
              }).fail(function () {
                  console.log("fail");
              });
          });
      });
    

    deleteFile 方法返回一个带有 .done 或 .fail 回调的 Promise。

    这允许您在所有延期完成后采取行动。就我而言,我将显示删除文件错误摘要。

    我刚试过这个,不幸的是我不得不设置一个间隔计时器来检查它们是否在我的 $.each 延迟对象上真正完成。这看起来很奇怪且违反直觉。

    仍在尝试理解这些延迟!

    【讨论】:

    • 如果任何延迟失败,也总是会“短路”。
    【解决方案4】:

    非常老的问题,但现在,要等到所有解决,您可以使用Promise.allSettled,因为$.ajax 处理标准承诺。

    从 jQuery 1.5 开始,由 $.ajax() 返回的 jqXHR 对象实现了 Promise 接口,为它们提供了 Promise 的所有属性、方法和行为

    因此你可以使用

    Promise.allSettled([$.ajax(), $.ajax()])
      .then((res) => {
        if (res[0].status === 'fulfilled') {
          console.log(res[0].value)
          // do something
        } else {
          console.error('res1 unavailable')
        }
    
        if (res[1].status === 'fulfilled') {
          console.log(res[1].value)
          // do something
        } else {
          console.error('res2 unavailable')
        }
      })
    

    【讨论】:

    • 除了IE 11不支持allSettled
    • @IanKemp 很有趣,我不知道,无论如何,我不需要 IE,但这很好。
    • 正是我需要的!谢谢。
    猜你喜欢
    • 1970-01-01
    • 2013-02-11
    • 2015-06-19
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    相关资源
    最近更新 更多