将延迟数组传递到 $.when()

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

网页:

<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>");
        });
    });
});

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

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


答案 1

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

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

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

在 ES6 中,您可以改用点差运算符...

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

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


答案 2

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

$.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)]);
            });
        });
    }
}

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

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