Pase una serie de Diferidos a $.when()

Resuelto adamjford asked hace 13 años • 9 respuestas

Aquí hay un ejemplo artificial de lo que está pasando: 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>");
        });
    });
});

Quiero "¡Todo listo!" aparece después de que se hayan completado todas las tareas diferidas, pero $.when()no parece saber cómo manejar una serie de objetos diferidos. "¡Todo listo!" está sucediendo primero porque la matriz no es un objeto diferido, por lo que jQuery sigue adelante y asume que ya está hecho.

Sé que se podrían pasar los objetos a la función, $.when(deferred1, deferred2, ..., deferredX)pero se desconoce cuántos objetos diferidos habrá en la ejecución del problema real que estoy tratando de resolver.

adamjford avatar Apr 12 '11 03:04 adamjford
Aceptado

Para pasar una matriz de valores a cualquier función que normalmente espera que sean parámetros separados, use Function.prototype.apply, por lo que en este caso necesita:

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

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

En ES6, puedes usar el ... operador de extensión en su lugar:

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

En cualquier caso, dado que es poco probable que sepa de antemano cuántos parámetros formales .thenrequerirá el controlador, ese controlador deberá procesar la argumentsmatriz para recuperar el resultado de cada promesa.

Alnitak avatar Apr 11 '2011 20:04 Alnitak

Las soluciones alternativas anteriores (¡gracias!) no abordan adecuadamente el problema de recuperar los objetos proporcionados al resolve()método diferido porque jQuery llama a las devoluciones de llamada done()y fail()con parámetros individuales, no con una matriz. Eso significa que tenemos que usar la argumentspseudomatriz para obtener todos los objetos resueltos/rechazados devueltos por la matriz de diferidos, lo cual es feo:

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

Dado que pasamos una serie de resultados diferidos, sería bueno obtener una serie de resultados. También sería bueno recuperar una matriz real en lugar de una pseudomatriz para que podamos usar métodos como Array.sort().

Aquí hay una solución inspirada en el método de when.jswhen.all() que aborda estos problemas:

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

Ahora puedes simplemente pasar una serie de aplazamientos/promesas y recuperar una serie de objetos resueltos/rechazados en tu devolución de llamada, así:

$.when.all(deferreds).then(function(objects) {
    console.log("Resolved objects:", objects);
});
crispyduck avatar Apr 25 '2013 06:04 crispyduck

Puede aplicar el whenmétodo a su matriz:

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

$.when.apply($, arr);

¿Cómo se trabaja con una variedad de jQuery Deferreds?

Eli avatar Apr 11 '2011 20:04 Eli