【问题标题】:jquery deferred - wait until two calls completejquery deferred - 等到两个调用完成
【发布时间】:2013-02-12 03:24:07
【问题描述】:

我正在寻找一种在两次 ajax 调用完成后进行回调的方法:

$.when(
    call1(),
    call2()
).always(function() {
    // Here I want to be sure the two calls are done and to get their responses 
);

问题是其中一个调用可能会失败。所以,在我的代码中,总是会在不等待另一个调用的情况下调用。

如何等待两个调用都完成(成功或失败)?

【问题讨论】:

  • 你可能想看看 Promise api.jquery.com/promise
  • @BlueBird:怎么样?承诺需要我没有的 jquery 对象。可以举个例子吗?
  • @BlueBird:$.when 已经返回了一个承诺对象,即 OP 已经使用了承诺。
  • @FelixKling:$.when 对我不利,因为它不会等待两个调用完成,以防一个调用失败。
  • @Naor:我知道,我想向 BlueBird 解释他的评论是多余的。关于您的问题:我认为您必须实现自己的$.when,只有在所有承诺都被拒绝/解决后才会拒绝和解决。以下是$.when 的实现方式:github.com/jquery/jquery/blob/1.9.1/src/deferred.js#L91

标签: javascript jquery jquery-deferred


【解决方案1】:

下面是一些应该可以解决问题的方法:

$.whenAllDone = function() {
    var deferreds = [];
    var result = $.Deferred();

    $.each(arguments, function(i, current) {
        var currentDeferred = $.Deferred();
        current.then(function() {
            currentDeferred.resolve(false, arguments);
        }, function() {
            currentDeferred.resolve(true, arguments);
        });
        deferreds.push(currentDeferred);
    });

    $.when.apply($, deferreds).then(function() {
        var failures = [];
        var successes = [];

        $.each(arguments, function(i, args) {
            // If we resolved with `true` as the first parameter
            // we have a failure, a success otherwise
            var target = args[0] ? failures : successes;
            var data = args[1];
            // Push either all arguments or the only one
            target.push(data.length === 1 ? data[0] : args);
        });

        if(failures.length) {
            return result.reject.apply(result, failures);
        }

        return result.resolve.apply(result, successes);
    });

    return result;
}

查看this Fiddle 了解其工作原理。

基本上它会等待所有 Deferred 完成,无论它们是否失败并收集所有结果。如果我们有失败,则返回的 Deferred 将失败并列出所有失败,否则会以所有成功解决。

【讨论】:

  • 不。我认为更常见的情况是,如果其中任何一个失败,您不需要等待所有 Deferred 完成。
  • 那么假设 succes 参数的顺序与添加的 deferreds 的顺序相同是否正确?来自 dfd1 的参数 [0]?
  • 我想在你的小提琴上加一点。我仍然希望根据上述规则解决或拒绝 whenAllDone,但我还想要一份完整的报告,说明哪些承诺已解决/拒绝以及它返回的数据。所以我创造了这个,希望它对某人有用:jsfiddle.net/KnightOfShaddai/n2pu2fsq/4
  • 如果只有一个 deferred 传入,这似乎不起作用。有人有想法吗? jsfiddle.net/3h6gwe1x
【解决方案2】:

这不是很漂亮,但是您可以为每个 ajax 调用设置一个全局“已完成”变量,以便在完成时设置。每次调用还会检查是否设置了两个变量,如果是,则调用你的 always 函数。

【讨论】:

    【解决方案3】:

    你也可以嵌套调用:

    $.when(call1()).always(function(){
        $.when(call2()).always(function(){
            // Here I want to be sure the two calls are done and to get their responses
        });
    });
    

    当然,这两个调用会变得彼此同步。

    【讨论】:

      【解决方案4】:

      达夫的回答很好。只有一个问题。当只有一个 deferred 时,事情就不起作用了。

      问题出在 jquery 的 when 方法中。

      jquery.when: function( subordinate /* , ..., subordinateN */ ) { ...

      它有这样一行:

      // If resolveValues consist of only a single Deferred, just use that.
      deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
      

      这改变了参数的形状,所以我不得不把它放回我的代码期望的通用形状(即当多个延迟传递给whenAllDone时相同的形状)

      const jqueryWhenUsesSubordinate = deferreds.length == 1;
      
      const deferredArgs = jqueryWhenUsesSubordinate
          ? [[ arguments[ 0 ], arguments[ 1 ] ]]
          : arguments
      
      $.each(deferredArgs, function (i, resolvedArgs) {
          var target = !resolvedArgs[0] ? failures : successes;
          var data = resolvedArgs[1];
          target.push(data.length === 1 ? data[0] : data);
      });
      
      

      此外,我更改了函数签名以更接近地匹配Promise.allSettled,因为它应该采用延迟对象的数组参数,然后不是循环遍历arguments 来设置deferreds 数组,而是循环遍历传入的那个参数。

      这允许您以编程方式在数组中创建可变长度的延迟,并将其传递给whenAllDone

      【讨论】:

        猜你喜欢
        • 2014-04-05
        • 2017-08-08
        • 2011-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多