465

这是一个人为的例子: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)但不知道在我试图解决的实际问题中执行时会有多少延迟对象。

4

9 回答 9

761

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

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

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

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

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

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

于 2011-04-11T20:41:10.557 回答
112

上面的解决方法(谢谢!)没有正确解决获取提供给 deferredresolve()方法的对象的问题,因为 jQuery使用单独的参数而不是数组调用done()and回调。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)]);
            });
        });
    }
}

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

$.when.all(deferreds).then(function(objects) {
    console.log("Resolved objects:", objects);
});
于 2013-04-25T06:53:16.180 回答
38

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

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

$.when.apply($, arr);

你如何使用 jQuery Deferreds 数组?

于 2011-04-11T20:48:01.087 回答
8

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

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

例子

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>

于 2015-07-18T20:18:12.370 回答
5

作为一个不需要$.when.apply或 的简单替代方案,array您可以使用以下模式为多个并行 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.then(newpromise)
  • 缺点是它在幕后创建了额外的 Promise 对象,并且最后传递的任何参数都不是很有用(因为它们嵌套在其他对象中)。尽管它简短而简单,但对于您想要的。
  • 好处是它不需要阵列或阵列管理。
于 2015-05-08T19:09:57.227 回答
4

我想使用 $.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;
        }
    }
    
  2. 我们使用 ajax 创建函数数组以发送的部分代码:

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

    $.when(
        $.each(arrayOfFn, function(index, value) {
            value.call()
        })
    ).then(function() {
            alert("Cheer!");
        }
    )
    
于 2016-09-22T09:24:19.103 回答
1

如果您正在编译并可以访问 ES6,则可以使用扩展语法,该语法专门将对象的每个可迭代项作为离散参数应用,这正是$.when()需要的方式。

$.when(...deferreds).done(() => {
    // do stuff
});

MDN 链接 - 传播语法

于 2016-10-19T21:05:31.667 回答
0

我有一个非常相似的案例,我在每个循环中发布,然后根据从 ajax 收到的数字在某些字段中设置 html 标记。然后我需要对这些字段的(现在更新的)值求和,并放在一个总字段中。

因此,问题是我试图对所有数字进行求和,但尚未从异步 ajax 调用返回数据。我需要在几个函数中完成这个功能才能重用代码。我的外部函数会等待数据,然后再使用完全更新的 DOM 做一些事情。

    // 1st
    function Outer() {
        var deferreds = GetAllData();

        $.when.apply($, deferreds).done(function () {
            // now you can do whatever you want with the updated page
        });
    }

    // 2nd
    function GetAllData() {
        var deferreds = [];
        $('.calculatedField').each(function (data) {
            deferreds.push(GetIndividualData($(this)));
        });
        return deferreds;
    }

    // 3rd
    function GetIndividualData(item) {
        var def = new $.Deferred();
        $.post('@Url.Action("GetData")', function (data) {
            item.html(data.valueFromAjax);
            def.resolve(data);
        });
        return def;
    }
于 2017-03-28T10:22:13.910 回答
-1

如果您使用的是 angularJS 或 Q Promise 库的某些变体,那么您有一种.all()方法可以解决这个确切的问题。

var savePromises = [];
angular.forEach(models, function(model){
  savePromises.push(
    model.saveToServer()
  )
});

$q.all(savePromises).then(
  function success(results){...},
  function failed(results){...}
);

查看完整的 API:

https://github.com/kriskowal/q/wiki/API-Reference#promiseall

https://docs.angularjs.org/api/ng/service/$q

于 2014-07-21T19:56:02.183 回答