javascript jquery argument-passing jquery-deferred .when

javascript - Pase una serie de diferidos a $.when()



jquery argument-passing (9)

Al llamar a múltiples llamadas AJAX paralelas, tiene dos opciones para manejar las respuestas respectivas.

  1. Utilice la llamada AJAX síncrona / una tras otra / no se recomienda
  2. Use Promises'' matriz de Promises'' y $.when acepte las promise y su devolución de llamada .done se .done cuando todas las promise se devuelven con éxito con las respuestas respectivas.

Ejemplo

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>

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 todas las tareas diferidas se hayan completado, pero $.when() no parece saber cómo manejar una matriz de objetos diferidos. "¡Todo listo!" ocurre primero porque la matriz no es un objeto diferido, por lo que jQuery sigue adelante y asume que se acaba de hacer.

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


Como una alternativa simple, que no requiere $.when.apply o una array , puede usar el siguiente patrón para generar una promesa única para múltiples promesas paralelas:

promise = $.when(promise, anotherPromise);

p.ej

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

Notas:

  • Me di cuenta de esto después de ver a alguien prometer la cadena de forma secuencial, utilizando la promise = promise.then(newpromise)
  • La desventaja es que crea objetos de promesa adicionales detrás de las escenas y cualquier parámetro que se pase al final no es muy útil (ya que están anidados dentro de objetos adicionales). Por lo que quieras aunque sea corto y sencillo.
  • La ventaja es que no requiere matriz o administración de matriz.

Las soluciones anteriores (¡gracias!) No resuelven correctamente el problema de recuperar los objetos proporcionados al método de resolve() diferida resolve() porque jQuery llama a las devoluciones de llamada done() y fail() con parámetros individuales, no a una matriz. Eso significa que tenemos que usar los arguments pseudo-matriz para obtener todos los objetos resueltos / rechazados devueltos por la matriz de aplazados, que es feo:

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

Desde que aprobamos una serie de aplazados, sería bueno recuperar una serie de resultados. También sería bueno recuperar una matriz real en lugar de una pseudo-matriz para que podamos usar métodos como Array.sort() .

Aquí hay una solución inspirada en el método when.all() que resuelve 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( function () { def.resolveWith(this, [Array.prototype.slice.call(arguments)]); }, function () { def.rejectWith(this, [Array.prototype.slice.call(arguments)]); }); }); } }

Ahora simplemente puede pasar una serie de aplazamientos / promesas y recuperar una serie de objetos resueltos / rechazados en su devolución de llamada, de esta manera:

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


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, puede utilizar el operador de propagación ... lugar:

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

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



Quiero proponer otra con $ .each:

  1. Podemos declarar una función ajax como:

    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. Parte del código donde creamos una matriz de funciones con ajax para enviar:

    var arrayOfFn = []; for (var i = 0; i < someDataArray.length; i++) { var ajaxFnForArray = new ajaxFn(someDataArray[i]); arrayOfFn.push(ajaxFnForArray); }

  3. Y llamando a funciones con el envío de ajax:

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



Si realiza la transcripción y tiene acceso a ES6, puede usar la sintaxis de propagación que aplica específicamente cada elemento iterable de un objeto como un argumento discreto, tal como lo necesita $.when() .

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

MDN Link - Sintaxis de propagación


Tuve un caso muy similar en el que estaba publicando en cada bucle y luego configurando el marcado html en algunos campos de los números recibidos del ajax. Entonces necesitaba hacer una suma de los valores (ahora actualizados) de estos campos y colocarlos en un campo total.

Por lo tanto, el problema era que estaba tratando de hacer una suma de todos los números, pero aún no había llegado ningún dato desde las llamadas async ajax. Necesitaba completar esta funcionalidad en algunas funciones para poder reutilizar el código. Mi función externa espera los datos antes de que luego vaya y haga algunas cosas con el DOM completamente actualizado.

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