这里有一个关于正在发生的事情的人为例子: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>");
        });
    });
});

我希望在所有延迟任务完成后显示“All done!”,但$.when()似乎不知道如何处理延迟对象数组。“All done!”首先发生,因为数组不是Deferred对象,因此jQuery继续执行并假定它已经完成。

我知道可以将对象传递到函数中,比如$。当(deferred1, deferred2,…, deferredX),但在我试图解决的实际问题中,不知道执行时会有多少个Deferred对象。


要将值数组传递给任何通常希望它们是单独参数的函数,请使用function .prototype。应用,所以在这种情况下,你需要:

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

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

在ES6中,你可以使用…展开运算符改为:

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

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


你可以将when方法应用到你的数组:

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

$.when.apply($, arr);

如何使用jQuery延迟数组?


上面的解决方法(谢谢!)并没有正确地解决返回提供给deferred的resolve()方法的对象的问题,因为jQuery使用单独的参数调用done()和fail()回调,而不是数组。这意味着我们必须使用参数pseudo-array来获取由deferreds数组返回的所有已解决/被拒绝的对象,这很难看:

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

因为我们传递了一个延迟数组,所以最好返回一个结果数组。得到一个实际的数组而不是伪数组也会很好,这样我们就可以使用array .sort()这样的方法。

下面是一个受到when.js的when.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)]);
            });
        });
    }
}

现在你可以简单地传入一个deferred /promises数组,并在回调中得到一个resolved/rejected对象数组,如下所示:

$.when.all(deferreds).then(function(objects) {
    console.log("Resolved objects:", objects);
});

如果你正在使用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/美元问


作为一种简单的替代方法,它不需要$.when。Apply或数组,你可以使用下面的模式为多个并行promise生成一个promise:

promise = $.when(promise, anotherPromise);

e.g.

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) 缺点是它在幕后创建了额外的承诺对象,并且在最后传递的任何参数都不是很有用(因为它们嵌套在额外的对象中)。对于你想要的东西,它是简短而简单的。 优点是它不需要数组或数组管理。


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

使用同步AJAX调用/一个接一个/不推荐 使用Promises的数组和$。当which接受承诺时,当所有承诺都成功返回时,它的回调.done将被调用。

例子

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>


我想建议使用$.each的另一个方法:

We may to declare ajax function like: 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; } } Part of code where we creating array of functions with ajax to send: var arrayOfFn = []; for (var i = 0; i < someDataArray.length; i++) { var ajaxFnForArray = new ajaxFn(someDataArray[i]); arrayOfFn.push(ajaxFnForArray); } And calling functions with sending ajax: $.when( $.each(arrayOfFn, function(index, value) { value.call() }) ).then(function() { alert("Cheer!"); } )


如果你正在编译并且可以访问ES6,你可以使用扩展语法,它专门将对象的每个可迭代项作为一个离散参数应用,就像$.when()需要它的方式一样。

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

MDN链接传播语法


我有一个非常类似的情况下,我张贴在每个循环,然后设置html标记在一些字段从数字从ajax接收。然后,我需要对这些字段的值(现在更新了)进行求和,并将其放置在total字段中。

因此,问题是,我试图对所有的数字做一个和,但没有数据从异步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;
    }