javascript jQuery.when - 当所有延迟不再“未解决”(已解决或被拒绝)时的回调?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5824615/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 18:29:22  来源:igfitidea点击:

jQuery.when - Callback for when ALL Deferreds are no longer 'unresolved' (either resolved or rejected)?

javascriptjqueryrestjquery-deferred

提问by Alan Spacer

When multiple Deferred objects are passed to jQuery.when, the method returns the Promise from a new "master" Deferred object that tracks the aggregate state of all the Deferreds it has been passed.

当多个 Deferred 对象传递给jQuery.when 时,该方法从一个新的“主”Deferred 对象返回 Promise,该对象跟踪它已传递的所有 Deferred 的聚合状态。

The method will either

该方法将要么

  1. resolve its master Deferred as soon as ALL the Deferreds resolve, or
  2. reject its master Deferred as soon as ONE of the Deferreds is rejected.
  1. 一旦所有延迟解决,就解决其主延迟,或
  2. 一旦其中一个 Deferred 被拒绝,就拒绝其主 Deferred。

If the master Deferred is resolved (ie. ALL the Deferreds resolve), it is passed the resolved values of all the Deferreds that were passed to jQuery.when. For example, when the Deferreds are jQuery.ajax() requests, the arguments will be the jqXHR objects for the requests, in the order they were given in the argument list:

如果主 Deferred 被解析(即所有 Deferred 解析),它会传递所有传递给 jQuery.when 的 Deferred 的解析值。例如,当延迟是 jQuery.ajax() 请求时,参数将是请求的 jqXHR 对象,按照它们在参数列表中给出的顺序:

$.when( $.getJSON('foo'), $.getJSON('bar') ).done(function(foo, bar) {

    // foo & bar are jqXHR objects for the requests

});

In the multiple Deferreds case where one of the Deferreds is rejected, jQuery.when IMMEDIATELY FIRES the fail callbacks for its master Deferred, even if some of the Deferreds may still be unresolved at that point:

在多个延迟的情况下,其中一个延迟被拒绝,jQuery.when 立即触发其主延迟的失败回调,即使此时某些延迟可能仍未解决:

$.when( $.getJSON('foo'), $.getJSON('bar') ).fail(function(req) {

    // req is the jqXHR object for one of the failed requests

});

I need to fire a callback when all the Deferreds passed to jQuery.when are no longer 'unresolved' (ie. all are either 'resolved' or 'rejected'). I could send JSON objects with 200 OK codes (instead sending JSON with 404 Not Found error status codes) and determine success/error in the done() method, but I'd prefer keeping my API RESTful. How can I accomplish this?

当所有传递给 jQuery.when 的 Deferreds 不再是“未解决”(即所有都是“已解决”或“拒绝”)时,我需要触发回调。我可以发送带有 200 个 OK 代码的 JSON 对象(而不是发送带有 404 Not Found 错误状态代码的 JSON)并在 done() 方法中确定成功/错误,但我更喜欢保持我的 API RESTful。我怎样才能做到这一点?

回答by Alnitak

I think the easiest way to do this is to keep a secondary Deferredobject around for each AJAX request, and ensure that thatone is always resolved:

我认为,要做到这一点最简单的方法就是保持辅助Deferred对象周围的每个AJAX请求,并确保一个总是解决:

var d1 = $.Deferred();
var d2 = $.Deferred();

var j1 = $.getJSON(...).complete(d1.resolve);
var j2 = $.getJSON(...).complete(d2.resolve);

$.when(j1, j2).done( only fires if j1 AND j2 are resolved );

$.when(d1, d2).done(function() {
     // will fire when j1 AND j2 are both resolved OR rejected
     // check j1.isResolved() and j2.isResolved() to find which failed
});

This is making use of the additional AJAX .complete()method which jQuery adds to its promises for AJAX methods, which is called for both resolved and rejected promises.

这是利用额外的 AJAX.complete()方法,jQuery 添加到其 AJAX 方法的承诺中,该方法对已解决和被拒绝的承诺均调用。

NB: d1.resolveworks as a callback in its own right, it doesn't need to be wrapped in a function() { ... }block.

注意:d1.resolve它本身就是一个回调,它不需要被包裹在一个function() { ... }块中。

回答by DazWilkin

@Alnitak answer is clever and helped me erase a hack that I'd created in which I was somewhat artificially resolving a promise - regardless of underlying outcome - in order that I may use 'when' to batch up multiple requests and use 'done' to proceed regardless of their success/failure.

@Alnitak 的回答很聪明,并帮助我消除了我创建的一个 hack,在该 hack 中我有点人为地解决了一个承诺 - 无论潜在的结果如何 - 为了我可以使用“when”来批量处理多个请求并使用“done”无论成功/失败,都继续前进。

I'm "answering" Alnitak's answer in the hope of providing another use for his suggestion that supports an arbitrary number of underlying promises.

我正在“回答”Alnitak 的回答,希望为他的建议提供另一种用途,该建议支持任意数量的潜在承诺。

var asyncFunc, entity, entities, $deferred, $deferreds;
// ...
foreach (entity in entities) {
    $deferred = $.Deferred();
    $deferreds.push($deferred);
    asyncFunc(entity).done(...).fail(...).always($deferred.resolve);
}
// ...
$.when.apply($, $deferreds).done(...)

This is pseudo-JavaScript but, it should convey the approach. For some arbitrarily sized set of entities, create a deferred ($deferred) for each entity and push it onto an array ($deferreds), make the async call, add done/fail as desired but always include an 'always' that resolves this entity's $deferred. NBthe 'always' receives the deferred's resolve function not its invocation.

这是伪 JavaScript,但它应该传达这种方法。对于一些任意大小的实体集,为每个实体创建一个延迟($deferred)并将其推送到一个数组($deferreds)上,进行异步调用,根据需要添加完成/失败,但始终包含一个“总是”来解决这个问题实体的 $deferred。注意“总是”接收延迟的解析函数而不是它的调用。

The 'when' converts the $deferreds array into the argument list for 'when' and, since this set of deferreds is guaranteed to resolve (thanks to the always), it's now possible to define a 'done' that will be invoked once all the async calls complete regardless of whether these succeed/fail.

'when' 将 $deferreds 数组转换为 'when' 的参数列表,并且由于这组延迟可以保证解析(感谢 always),现在可以定义一个将被调用一次的 'done'无论这些是否成功/失败,异步调用都会完成。

回答by fearphage

I've recently made a plugin that may help. I call it $.whenAll.

我最近制作了一个可能有帮助的插件。我称之为$.whenAll

This extension treats all successes and failures as progress events. After all the promises have completed, the global promise is resolved if there were no errors. Otherwise the global promise is rejected.

此扩展将所有成功和失败视为进度事件。在所有的 Promise 完成后,如果没有错误,全局 Promise 就会被解析。否则全局承诺被拒绝。

$.whenAll - https://gist.github.com/4341799(tests)

$.whenAll - https://gist.github.com/4341799测试

Sample usage:

示例用法:

$.whenAll($.getJSON('foo'), $.getJSON('bar'))
  .then(
    doneCallback
    ,failcallback
    // progress callback
    // the only problem is $.ajax.done/fail states call their callbacks 
    // with params in different locations (except for state)
    ,function(data, state, jqXhr) {
      if (state == 'success') {
        // do happy stuff
      }
      else { // error (fail)
        // `data` is actually the jqXhr object for failed requests
        // `jqXhr` is the text of the error "Not Found" in this example
      }
    }
  )
;

回答by leojh

My implementation:

我的实现:

Plugin Code:

插件代码:

jQuery.whenAll = function (deferreds) {
        var lastResolved = 0;

        var wrappedDeferreds = [];

        for (var i = 0; i < deferreds.length; i++) {
            wrappedDeferreds.push(jQuery.Deferred());

            deferreds[i].always(function() {
                wrappedDeferreds[lastResolved++].resolve(arguments);
            });
        }

        return jQuery.when.apply(jQuery, wrappedDeferreds).promise();
    };

To use it:

要使用它:

jQuery.whenAll([jQuery.get('/your-resource'), jQuery.get('/your-resource')])
   .done(
       function(result1, result2) {
           console.log(result1[1]);
           console.log(result2[1]);
       });

Check out the fiddle:http://jsfiddle.net/LeoJH/VMQ3F/

查看小提琴:http : //jsfiddle.net/LeoJH/VMQ3F/

回答by hippietrail

Here's a jQuery plugin I've made by modifying the actual core code for $.when()to use your semantics. For want of a better name it's called $.myWhen():

这是我通过修改实际核心代码$.when()来使用您的语义制作的 jQuery 插件。想要一个更好的名字,它被称为$.myWhen()

(function($) {
  $.myWhen = function( subordinate /* , ..., subordinateN */ ) {
    var i = 0,
      responseValues = Array.prototype.slice.call( arguments ),
      length = responseValues.length,

      // the count of uncompleted subordinates
      remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,

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

      // Update function for all resolve, reject and progress values
      updateFunc = function( i, contexts, values ) {
        return function( value ) {
          contexts[ i ] = this;
          values[ i ] = arguments.length > 1 ? Array.prototype.slice.call( arguments ) : value;
          if( values === progressValues ) {
            deferred.notifyWith( contexts, values );
          } else if ( !( --remaining ) ) {
            deferred.resolveWith( contexts, values );
          }
        };
      },

      progressValues, progressContexts, responseContexts;

    // add listeners to Deferred subordinates; treat others as resolved
    if ( length > 1 ) {
      progressValues = new Array( length );
      progressContexts = new Array( length );
      responseContexts = new Array( length );
      for ( ; i < length; i++ ) {
        if ( responseValues[ i ] && jQuery.isFunction( responseValues[ i ].promise ) ) {
          responseValues[ i ].promise()
            .always( updateFunc( i, responseContexts, responseValues ) )
            .progress( updateFunc( i, progressContexts, progressValues ) );
        } else {
          --remaining;
        }
      }
    }

    // if we're not waiting on anything, resolve the master
    if ( !remaining ) {
      deferred.resolveWith( responseContexts, responseValues );
    }

    return deferred.promise();
  };
})(jQuery);

Just put this code right after where you've loaded jQuery and the $.myWhen()function will be available alongside $.when(). Everything else is 100% exactly the same except for the semantics.

只需将此代码放在您加载 jQuery 的位置之后,该$.myWhen()函数就会与$.when(). 除了语义之外,其他一切都是 100% 完全相同的。

回答by baohouse

An improvement on Leo Hernandez's solution for more general use cases that don't simply involve fetching resources from a server, which for example can include events triggered by user interactions, or asynchronous jQuery UI calls (e.g. slideUp() and slideDown()). See https://jsfiddle.net/1trucdn3/for enhanced use case.

Leo Hernandez 解决方案的改进,适用于更一般的用例,这些用例不仅仅涉及从服务器获取资源,例如可以包括由用户交互或异步 jQuery UI 调用(例如 slideUp() 和 slideDown())触发的事件。有关增强的用例,请参阅https://jsfiddle.net/1trucdn3/

$.whenAll = function (deferreds) {
    var lastResolved = 0;
    var wrappedDeferreds = [];

    for (var i = 0; i < deferreds.length; i++) {
        wrappedDeferreds.push($.Deferred());
        if (deferreds[i] && deferreds[i].always) {
            deferreds[i].always(wrappedDeferreds[lastResolved++].resolve);
        } else {
            wrappedDeferreds[lastResolved++].resolve(deferreds[i]);
        }
    }

    return $.when.apply($, wrappedDeferreds).promise();
};

The improvement allows us to pass in non-Deferred values into the array argument. This was something that you could do with $.when(). Also, I cleaned up the output you get in the callback function to be more inline with how the original $.when() method works, in case you just want to get back the result regardless of the status. Leo's solution would pass the entire deferred object as a result, which you then need to dig into to find the information you need.

改进允许我们将非延迟值传递到数组参数中。这是你可以用 $.when() 做的事情。此外,我清理了您在回调函数中获得的输出,使其与原始 $.when() 方法的工作方式更加一致,以防您只想获取结果而不管状态如何。Leo 的解决方案会因此传递整个延迟对象,然后您需要深入研究以找到所需的信息。

$.whenAll([1, $.Deferred().resolve("Good"), $.Deferred().reject("Bad")])
    .done(function (result1, result2, result3) {
        // result1 -> 1
        // result2 -> "Good"
        // result3 -> "Bad"
    });

回答by lagivan

@Alnitak and @DazWilkin answers are great! But I personally prefer functional style so here is a functional version for arbitrary number of promises:

@Alnitak 和 @DazWilkin 的回答很棒!但我个人更喜欢函数式风格,所以这里有一个用于任意数量承诺的函数式版本:

var entities;
// ...
var deferreds = entities.map(function() {
    var deferred = $.Deferred();
    asyncFunc(this).done(...).fail(...).always(deferred.resolve);
    return deferred;
}
// ...
$.when.apply($, deferreds).done(...)

Compared to @DazWilkin answer, I use mapfunction instead of foreach.

与@DazWilkin 的回答相比,我使用map函数而不是foreach.

回答by Tom Schreck

I found a solution where I have 2 requests in a when and am able to access individual successes even when one of the requests fail:

我找到了一个解决方案,其中我有 2 个请求,即使其中一个请求失败,也能够访问单个成功:

        $.when
        (
            $.getJSON(...).then(function (results)
            {
                console.log('SUCCESS REQUEST 1 BY ITSELF', results);
            }),
            $.getJSON(...).then(function (results)
            {
                console.log('SUCCESS REQUEST 2 BY ITSELF', results);
            })
        ).then
        (
            function (results1, results2)
            {
                console.log('BOTH REQUESTS SUCCESSFUL...');
                console.log('results1', results1);
                console.log('results2', results2);
            },
            function (error1, error2)
            {
                console.log('AT LEAST 1 REQUEST FAILED...');
                console.log('error1', error1);
                console.log('error2', error2);                  
            }
        );