将一组延迟传递给 $.when()

IT技术 javascript jquery argument-passing jquery-deferred .when
2021-01-16 06:10:13

这是正在发生的事情的人为示例:http : //jsfiddle.net/adamjford/YNGcm/20/

HTML:

<a href="#">Click me!</a>
<div></div>

JavaScript:

function getSomeDeferredStuff() {
    var deferreds = [];

    var i = 1;
    for (i = 1; i <= 10; i++) {
        var count = i;

        deferreds.push(
        $.post('/echo/html/', {
            html: "<p>Task #" + count + " complete.",
            delay: count
        }).success(function(data) {
            $("div").append(data);
        }));
    }

    return deferreds;
}

$(function() {
    $("a").click(function() {
        var deferreds = getSomeDeferredStuff();

        $.when(deferreds).done(function() {
            $("div").append("<p>All done!</p>");
        });
    });
});

我要“全部完成!” 在所有延迟任务完成后$.when()出现,但似乎不知道如何处理延迟对象数组。“全做完了!” 因为数组不是 Deferred 对象,所以首先发生,所以 jQuery 继续并假设它刚刚完成。

我知道可以将对象传递给函数,$.when(deferred1, deferred2, ..., deferredX)但不知道在我试图解决的实际问题中将有多少延迟对象在执行。

6个回答

要将一组值传递给通常希望它们是单独参数的任何函数,请使用Function.prototype.apply,因此在这种情况下,您需要:

$.when.apply($, my_array).then( ___ );

http://jsfiddle.net/YNGcm/21/

在 ES6 中,您可以改用... 展开运算符

$.when(...my_array).then( ___ );

在任何一种情况下,由于您不太可能事先知道.then处理程序需要多少正式参数,因此该处理程序需要处理arguments数组以检索每个Promise的结果。

@Alnitak:考虑到我现在编写 JavaScript 已经有多久了,我有点尴尬,因为我不知道这种方法!
2021-03-12 06:10:13
@Alnitak:是的,但是$打字量比实现更改null时要,而且您在$.when实现更改时很安全(在这种情况下不太可能,但为什么this默认情况下不保持不变)。
2021-03-17 06:10:13
这有效,太棒了。:) 我很惊讶我无法通过 Google 进行如此简单的更改!
2021-03-18 06:10:13
FWIW,Eli 对早期问题的回答中的链接,其中讨论了将$vsnull作为第一个参数的讨论,值得一读。不过,在这种特殊情况下,这并不重要。
2021-03-28 06:10:13
这是因为它是一个通用的方法,而不是具体到$.when-f.apply(ctx, my_array)将调用fthis == ctx并设置为参数内容my_array
2021-03-30 06:10:13

上面的解决方法(谢谢!)没有正确解决取回提供给 deferredresolve()方法的对象的问题,因为 jQuery使用单个参数而不是数组调用done()fail()回调。这意味着我们必须使用arguments伪数组来获取延迟数组返回的所有已解决/拒绝的对象,这很丑陋:

$.when.apply($,deferreds).then(function() {
     var objects = arguments; // The array of resolved objects as a pseudo-array
     ...
};

因为我们传入了一个延迟数组,所以返回一个结果数组会很好。返回一个实际数组而不是伪数组也很好,这样我们就可以使用像Array.sort().

这里是通过激发溶液when.jswhen.all()方法,该方法解决了这些问题:

// Put somewhere in your scripting environment
if (typeof jQuery.when.all === 'undefined') {
    jQuery.when.all = function (deferreds) {
        return $.Deferred(function (def) {
            $.when.apply(jQuery, deferreds).then(
            // the calling function will receive an array of length N, where N is the number of
            // deferred objects passed to when.all that succeeded. each element in that array will
            // itself be an array of 3 objects, corresponding to the arguments passed to jqXHR.done:
            // ( data, textStatus, jqXHR )
            function () {
                var arrayThis, arrayArguments;

                if (Array.isArray(this)) {
                    arrayThis = this;
                    arrayArguments = arguments;
                }
                else {
                    arrayThis = [this];
                    arrayArguments = [arguments];
                }

                def.resolveWith(arrayThis, [Array.prototype.slice.call(arrayArguments)]);
            },
            // the calling function will receive an array of length N, where N is the number of
            // deferred objects passed to when.all that failed. each element in that array will
            // itself be an array of 3 objects, corresponding to the arguments passed to jqXHR.fail:
            // ( jqXHR, textStatus, errorThrown )
            function () {
                var arrayThis, arrayArguments;

                if (Array.isArray(this)) {
                    arrayThis = this;
                    arrayArguments = arguments;
                }
                else {
                    arrayThis = [this];
                    arrayArguments = [arguments];
                }

                def.rejectWith(arrayThis, [Array.prototype.slice.call(arrayArguments)]);
            });
        });
    }
}

现在您可以简单地传入一组延迟/Promise并在您的回调中取回一组已解决/拒绝的对象,如下所示:

$.when.all(deferreds).then(function(objects) {
    console.log("Resolved objects:", objects);
});
您的代码只有一个小问题,当数组中只有一个元素时,结果数组仅返回该结果,而不是具有单个元素的数组(这会破坏需要数组的代码)。要修复它,请使用此函数var toArray = function (args) { return deferreds.length > 1 ? $.makeArray(args) : [args]; }而不是Array.prototype.slice.call.
2021-03-22 06:10:13

您可以将该when方法应用于您的数组:

var arr = [ /* Deferred objects */ ];

$.when.apply($, arr);

你如何使用 jQuery Deferreds 数组?

我实际上看到了那个问题,但我想那个问题中的所有额外细节都导致我的问题(就在那里)的答案直接从我的头上飞过。
2021-03-26 06:10:13
@patridge:很高兴听到它帮助了你!
2021-03-27 06:10:13
@adamjford,如果它让您感觉好些,我发现您的问题更容易理解(首先是在我对这个确切问题的特定 Google 搜索中)。
2021-04-07 06:10:13
这是一个很好的答案,但我不清楚这如何应用于原始问题中的示例。在咨询了链接的问题后,很明显行“$.when(deferreds).done(function() {”应该简单地更改为“$.when.apply($,deferreds).done(function() {” “。 对?
2021-04-08 06:10:13

当调用多个并行 AJAX 调用时,您有两个选项来处理各自的响应。

  1. 使用同步 AJAX 调用/一个接一个/不推荐
  2. 使用Promises'array 并且$.when它接受promises 并且.done当所有promises 成功返回并带有相应的响应,它的回调被调用

例子

function ajaxRequest(capitalCity) {
   return $.ajax({
        url: 'https://restcountries.eu/rest/v1/capital/'+capitalCity,
        success: function(response) {
        },
        error: function(response) {
          console.log("Error")
        }
    });
}
$(function(){
   var capitalCities = ['Delhi', 'Beijing', 'Washington', 'Tokyo', 'London'];
   $('#capitals').text(capitalCities);

   function getCountryCapitals(){ //do multiple parallel ajax requests
      var promises = [];   
      for(var i=0,l=capitalCities.length; i<l; i++){
            var promise = ajaxRequest(capitalCities[i]);
            promises.push(promise);
      }
  
      $.when.apply($, promises)
        .done(fillCountryCapitals);
   }
  
   function fillCountryCapitals(){
        var countries = [];
        var responses = arguments;
        for(i in responses){
            console.dir(responses[i]);
            countries.push(responses[i][0][0].nativeName)
        }  
        $('#countries').text(countries);
   }
  
   getCountryCapitals()
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
  <h4>Capital Cities : </h4> <span id="capitals"></span>
  <h4>Respective Country's Native Names : </h4> <span id="countries"></span>
</div>

回复:2 - 问题是它可能会被其他无法保证的人复制,或者愚蠢到添加到Array.prototype. 无论如何,对于非性能关键代码,最好使用/循环.map代替,例如- job done。forpushvar promises = capitalCities.map(ajaxRequest); $.when.apply($, promises).then(fillCountryCapitals)
2021-03-10 06:10:13
我认为用例子详细解释会更好,有可用的选项。为此,我认为没有必要投票。
2021-03-14 06:10:13
1. 同意,应该有 (not recommended)2. 不同意 -for ... in可以,因为数组只包含那些需要的属性(没有额外的属性)。无论如何谢谢
2021-03-16 06:10:13
否决票是 1. 甚至建议同步(尽管建议不要) 2. 示例中的低质量代码(包括for ... in在数组上?!)
2021-03-22 06:10:13
您的回答超出了范围,您对问题标题的编辑也是如此。OP 已经知道如何进行 AJAX 调用并获得一组延迟对象。问题的唯一要点是如何将该数组传递给$.when.
2021-04-06 06:10:13

作为一个简单的替代方案,不需要$.when.applyarray,您可以使用以下模式为多个并行Promise生成单个Promise:

promise = $.when(promise, anotherPromise);

例如

function GetSomeDeferredStuff() {
    // Start with an empty resolved promise (or undefined does the same!)
    var promise;
    var i = 1;
    for (i = 1; i <= 5; i++) {
        var count = i;

        promise = $.when(promise,
        $.ajax({
            type: "POST",
            url: '/echo/html/',
            data: {
                html: "<p>Task #" + count + " complete.",
                delay: count / 2
            },
            success: function (data) {
                $("div").append(data);
            }
        }));
    }
    return promise;
}

$(function () {
    $("a").click(function () {
        var promise = GetSomeDeferredStuff();
        promise.then(function () {
            $("div").append("<p>All done!</p>");
        });
    });
});

笔记:

  • 在看到有人按顺序使用链式Promise后,我想出了这个 promise = promise.then(newpromise)
  • 缺点是它在幕后创建了额外的 promise 对象,并且最后传递的任何参数都不是很有用(因为它们嵌套在其他对象中)。对于你想要的,虽然它简短而简单。
  • 好处是它不需要阵列或阵列管理。
@halfer:我不再发帖了,但我对原始内容的无知感到恼火。现在把所有的新想法都留给自己:)
2021-03-15 06:10:13
@Alnitak:课程用马。你当然有权发表意见,但你自己显然没有使用过。我自己的观点是基于这种技术的实际应用。可以工作并有用途,那么为什么要根据“大量警告”(一个)和“什么都不解决”(不是真的 - 它消除了数组处理并简化了返回的并行Promise的链接)从工具箱中扔掉了一个工具值不是必需的,正如您应该知道的那样,无论如何在并行处理情况下很少使用)。反对票应该是“这个答案没有用”:)
2021-04-01 06:10:13
@AnthonyMcLin:这是为了提供一种更简单的编码替代方案,而不是更好的性能(大多数异步编码可以忽略不计),就像then()以类似方式链接调用一样。with 的行为$.when是因为它是并行的(不是链接的)。请在丢弃有用的替代方案之前尝试它,因为它确实有效:)
2021-04-02 06:10:13
嗨@GoneCoding。请问您不要在答案中添加投票评论吗?这适用于评论,但除此之外,噪音会分散对其他好的内容的注意力。谢谢。
2021-04-04 06:10:13
如果我错了,请纠正我,但你的方法是有效地嵌套 $.when( $.when( $.when(...) ) ) 所以如果有 10 次迭代,你最终会递归嵌套 10 层深。这似乎不是很平行,因为您必须等待每个级别返回子级的嵌套Promise,然后才能返回自己的Promise - 我认为已接受答案中的数组方法更清晰,因为它使用内置的灵活参数行为$.when() 方法。
2021-04-08 06:10:13