将Deferred数组传递给$ .when()

ada*_*ord 439 javascript jquery argument-passing jquery-deferred .when

这是一个人为的例子:http://jsfiddle.net/adamjford/YNGcm/20/

HTML:

<a href="#">Click me!</a>
<div></div>
Run Code Online (Sandbox Code Playgroud)

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>");
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

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

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

Aln*_*tak 719

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

$.when.apply($, my_array).then( ___ );
Run Code Online (Sandbox Code Playgroud)

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

在ES6中,您可以使用... 扩展运算符:

$.when(...my_array).then( ___ );
Run Code Online (Sandbox Code Playgroud)

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

  • 那是因为它是一个泛型方法,不是特定于`$ .when` - `f.apply(ctx,my_array)`将使用`this == ctx`调用`f`并将参数设置为`my_array`的_contents_. (8认同)
  • FWIW,Eli回答一个问题的回答,讨论传递`$`vs`null`作为第一个参数值得一读.但在这种特殊情况下,无关紧要. (5认同)
  • 这很棒,很棒.:)我很惊讶我无法通过Google挖掘这么简单的变化! (4认同)
  • @Alnitak:我有点尴尬,我不知道这个方法,考虑到我现在已经写了多长时间了! (4认同)
  • @Alnitak:是的,但是`$`比'null`更少输入,并且当'$ .when`实现发生变化时你很安全(不是在这种情况下很可能但是为什么不保持`this`默认不变). (4认同)

cri*_*uck 107

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

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

由于我们传入了一个延迟数组,因此返回一组结果会很好.回到实际的数组而不是伪数组也是很好的,所以我们可以使用类似的方法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(
                function () {
                    def.resolveWith(this, [Array.prototype.slice.call(arguments)]);
                },
                function () {
                    def.rejectWith(this, [Array.prototype.slice.call(arguments)]);
                });
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以简单地传入一个deferreds/promises数组,并在你的回调函数中找回一系列已解析/被拒绝的对象,如下所示:

$.when.all(deferreds).then(function(objects) {
    console.log("Resolved objects:", objects);
});
Run Code Online (Sandbox Code Playgroud)

  • 您可能希望使用resolveWith和rejectWith,因此您获得与'this'deferred.resolveWith(this,[Array.prototype.slice.call(arguments)])等相同的原始延迟. (6认同)

Eli*_*Eli 38

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

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

$.when.apply($, arr);
Run Code Online (Sandbox Code Playgroud)

你如何使用一组jQuery Deferreds?


vin*_*akj 7

调用多个并行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()
})
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

  • 我认为用例子详细解释会更好,有可用的选项.为此我不认为downvote是必要的. (4认同)
  • 您的回答超出了范围,您对问题标题的编辑也是如此。OP 已经知道如何进行 AJAX 调用并获取一组延迟对象。问题的 _sole_ 点是如何将该数组传递给 `$.when`。 (2认同)
  • 否决票是 1. 甚至建议同步(尽管建议不要) 2. 示例中的低质量代码(包括数组上的“for ... in”?!) (2认同)

Gon*_*ing 6

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

promise = $.when(promise, anotherPromise);
Run Code Online (Sandbox Code Playgroud)

例如

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>");
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 在看到某个连锁承诺依次使用后,我想出了这个 promise = promise.then(newpromise)
  • 缺点是它在幕后创建了额外的promise对象,并且在末尾传递的任何参数都不是很有用(因为它们嵌套在其他对象中).为了你想要的东西虽然它简短而简单.
  • 好处是它不需要阵列或阵列管理.

  • 如果我错了,请纠正我,但你的方法是有效地嵌套$ .when($ .when($ .when(...)))所以如果有10次迭代,你最终会递归嵌套10级深度.这似乎不是很平行,因为你必须等待每个级别返回子级的嵌套promise才能返回它自己的promise - 我认为接受答案中的数组方法更清晰,因为它使用内置的灵活参数行为$ .when()方法. (2认同)
  • @Alnitak:课程的马匹.你肯定有权获得意见,但你显然没有自己使用过.我自己的观点是基于这种技术的实际用途.它*工作*并且有用,所以为什么要从工具箱中抛出一个工具,例如"加载注意事项"(一个)和"什么都不解决"这样的夸张(不是真的 - 它消除了数组处理并简化了并行承诺的链接不需要返回值,无论如何,你应该知道并行处理情况很少使用.Downvotes应该是"这个答案没用":) (2认同)

小智 5

我想建议使用 $.each 另一种:

  1. 我们可以像这样声明 ajax 函数:

    function ajaxFn(someData) {
        this.someData = someData;
        var that = this;
        return function () {
            var promise = $.Deferred();
            $.ajax({
                method: "POST",
                url: "url",
                data: that.someData,
                success: function(data) {
                    promise.resolve(data);
                },
                error: function(data) {
                    promise.reject(data);
                }
            })
            return promise;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 我们使用 ajax 创建函数数组并发送的部分代码:

    var arrayOfFn = [];
    for (var i = 0; i < someDataArray.length; i++) {
        var ajaxFnForArray = new ajaxFn(someDataArray[i]);
        arrayOfFn.push(ajaxFnForArray);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 并通过发送 ajax 调用函数:

    $.when(
        $.each(arrayOfFn, function(index, value) {
            value.call()
        })
    ).then(function() {
            alert("Cheer!");
        }
    )
    
    Run Code Online (Sandbox Code Playgroud)