style script change attribute javascript jquery ajax design-patterns queue

javascript - script - title css



Secuencia de solicitudes de ajax (8)

jQuery 1.5+

Desarrollé un $.ajaxQueue() que usa api.jquery.com/category/deferred-object , .queue() y $.ajax() para devolver también una promise que se resuelve cuando la solicitud finaliza.

/* * jQuery.ajaxQueue - A queue for ajax requests * * (c) 2011 Corey Frang * Dual licensed under the MIT and GPL licenses. * * Requires jQuery 1.5+ */ (function($) { // jQuery on an empty object, we are going to use this as our Queue var ajaxQueue = $({}); $.ajaxQueue = function( ajaxOpts ) { var jqXHR, dfd = $.Deferred(), promise = dfd.promise(); // queue our ajax request ajaxQueue.queue( doRequest ); // add the abort method promise.abort = function( statusText ) { // proxy abort to the jqXHR if it is active if ( jqXHR ) { return jqXHR.abort( statusText ); } // if there wasn''t already a jqXHR we need to remove from queue var queue = ajaxQueue.queue(), index = $.inArray( doRequest, queue ); if ( index > -1 ) { queue.splice( index, 1 ); } // and then reject the deferred dfd.rejectWith( ajaxOpts.context || ajaxOpts, [ promise, statusText, "" ] ); return promise; }; // run the actual query function doRequest( next ) { jqXHR = $.ajax( ajaxOpts ) .done( dfd.resolve ) .fail( dfd.reject ) .then( next, next ); } return promise; }; })(jQuery);

jQuery 1.4

Si está utilizando jQuery 1.4, puede utilizar la cola de animación en un objeto vacío para crear su propia "cola" para sus solicitudes de ajax para los elementos.

Incluso puedes factorizar esto en tu propio reemplazo de $.ajax() . Este complemento $.ajaxQueue() usa la cola estándar ''fx'' para jQuery, que iniciará automáticamente el primer elemento agregado si la cola no se está ejecutando.

(function($) { // jQuery on an empty object, we are going to use this as our Queue var ajaxQueue = $({}); $.ajaxQueue = function(ajaxOpts) { // hold the original complete function var oldComplete = ajaxOpts.complete; // queue our ajax request ajaxQueue.queue(function(next) { // create a complete callback to fire the next event in the queue ajaxOpts.complete = function() { // fire the original complete if it was there if (oldComplete) oldComplete.apply(this, arguments); next(); // run the next query in the queue }; // run the query $.ajax(ajaxOpts); }); }; })(jQuery);

Ejemplo de uso

Entonces, tenemos un <ul id="items"> que tiene un <li> que queremos copiar (usando ajax!) Al <ul id="output">

// get each item we want to copy $("#items li").each(function(idx) { // queue up an ajax request $.ajaxQueue({ url: ''/echo/html/'', data: {html : "["+idx+"] "+$(this).html()}, type: ''POST'', success: function(data) { // Write to #output $("#output").append($("<li>", { html: data })); } }); });

demostración jsfiddle - versión 1.4

Encuentro que a veces necesito iterar alguna colección y hacer una llamada ajax para cada elemento. Quiero que cada llamada regrese antes de pasar al siguiente elemento para no criticar al servidor con solicitudes, lo que a menudo conduce a otros problemas. Y no quiero configurar asincrónico en falso y congelar el navegador.

Por lo general, esto implica la configuración de algún tipo de contexto de iterador que paso por paso en cada devolución de llamada exitosa. Creo que debe haber una manera más limpia y simple?

¿Alguien tiene un patrón de diseño inteligente sobre cómo trabajar ordenadamente a través de una colección haciendo llamadas ajax para cada artículo?


Estoy publicando esta respuesta pensando que podría ayudar a otras personas en el futuro, buscando algunas soluciones simples en el mismo escenario.

Esto ahora también es posible utilizando el soporte de promesas nativas introducido en ES6. Puede envolver la llamada ajax en una promesa y devolverla al controlador del elemento.

function ajaxPromise(elInfo) { return new Promise(function (resolve, reject) { //Do anything as desired with the elInfo passed as parameter $.ajax({ type: "POST", url: ''/someurl/'', data: {data: "somedata" + elInfo}, success: function (data) { //Do anything as desired with the data received from the server, //and then resolve the promise resolve(); }, error: function (err) { reject(err); }, async: true }); }); }

Ahora llama a la función recursivamente, desde donde tienes la colección de los elementos.

function callAjaxSynchronous(elCollection) { if (elCollection.length > 0) { var el = elCollection.shift(); ajaxPromise(el) .then(function () { callAjaxSynchronous(elCollection); }) .catch(function (err) { //Abort further ajax calls/continue with the rest //callAjaxSynchronous(elCollection); }); } else { return false; } }


Idealmente, una coroutine con múltiples puntos de entrada para que cada devolución de llamada desde el servidor pueda llamar a la misma coroutine será limpia. Maldición, esto está por implementarse en Javascript 1.7.

Déjame intentar usar el cierre ...

function BlockingAjaxCall (URL,arr,AjaxCall,OriginalCallBack) { var nextindex = function() { var i =0; return function() { return i++; } }; var AjaxCallRecursive = function(){ var currentindex = nextindex(); AjaxCall ( URL, arr[currentindex], function() { OriginalCallBack(); if (currentindex < arr.length) { AjaxCallRecursive(); } } ); }; AjaxCallRecursive(); } // suppose you always call Ajax like AjaxCall(URL,element,callback) you will do it this way BlockingAjaxCall(URL,myArray,AjaxCall,CallBack);


Puede ajustar toda esa complejidad en una función para hacer una simple llamada que se vea así:

loadSequantially([''/a'', ''/a/b'', ''a/b/c''], function() {alert(''all loaded'')});

A continuación se muestra un boceto aproximado (ejemplo de trabajo, excepto la llamada ajax). Esto se puede modificar para usar una estructura tipo cola en lugar de una matriz

// load sequentially the given array of URLs and call ''funCallback'' when all''s done function loadSequantially(arrUrls, funCallback) { var idx = 0; // callback function that is called when individual ajax call is done // internally calls next ajax URL in the sequence, or if there aren''t any left, // calls the final user specified callback function var individualLoadCallback = function() { if(++idx >= arrUrls.length) { doCallback(arrUrls, funCallback); }else { loadInternal(); } }; // makes the ajax call var loadInternal = function() { if(arrUrls.length > 0) { ajaxCall(arrUrls[idx], individualLoadCallback); }else { doCallback(arrUrls, funCallback); } }; loadInternal(); }; // dummy function replace with actual ajax call function ajaxCall(url, funCallBack) { alert(url) funCallBack(); }; // final callback when everything''s loaded function doCallback(arrUrls, func) { try { func(); }catch(err) { // handle errors } };


Puedes lograr lo mismo usando then .

var files = [ ''example.txt'', ''example2.txt'', ''example.txt'', ''example2.txt'', ''example.txt'', ''example2.txt'', ''example2.txt'', ''example.txt'' ]; nextFile().done(function(){ console.log("done",arguments) }); function nextFile(text){ var file = files.shift(); if(text) $(''body'').append(text + ''<br/>''); if(file) return $.get(file).then(nextFile); }

http://plnkr.co/edit/meHQHU48zLTZZHMCtIHm?p=preview


Sí, mientras que las otras respuestas funcionarán, son un montón de código y una apariencia desordenada. Frame.js fue diseñado para abordar elegantemente esta situación. https://github.com/bishopZ/Frame.js

Por ejemplo, esto hará que la mayoría de los navegadores se cuelguen:

for(var i=0; i<1000; i++){ $.ajax(''myserver.api'', { data:i, type:''post'' }); }

Si bien esto no:

for(var i=0; i<1000; i++){ Frame(function(callback){ $.ajax(''myserver.api'', { data:i, type:''post'', complete:callback }); }); } Frame.start();

Además, el uso de Frame le permite colocar los objetos de respuesta en cascada y lidiar con todos ellos una vez completada la serie completa de solicitudes de AJAX (si lo desea):

var listOfAjaxObjects = [ {}, {}, ... ]; // an array of objects for $.ajax $.each(listOfAjaxObjects, function(i, item){ Frame(function(nextFrame){ item.complete = function(response){ // do stuff with this response or wait until end nextFrame(response); // ajax response objects will waterfall to the next Frame() $.ajax(item); }); }); Frame(function(callback){ // runs after all the AJAX requests have returned var ajaxResponses = []; $.each(arguments, function(i, arg){ if(i!==0){ // the first argument is always the callback function ajaxResponses.push(arg); } }); // do stuff with the responses from your AJAX requests // if an AJAX request returned an error, the error object will be present in place of the response object callback(); }); Frame.start()


Una solución rápida y pequeña utilizando promesas diferidas. Aunque esto utiliza $.Deferred jQuery, cualquier otro debería hacer.

var Queue = function () { var previous = new $.Deferred().resolve(); return function (fn, fail) { return previous = previous.then(fn, fail || fn); }; };

Uso, llamada para crear nuevas colas:

var queue = Queue(); // Queue empty, will start immediately queue(function () { return $.get(''/first''); }); // Will begin when the first has finished queue(function() { return $.get(''/second''); });

Vea el ejemplo con una comparación lado a lado de las solicitudes asincrónicas.


Uso http://developer.yahoo.com/yui/3/io/#queue para obtener esa funcionalidad.

La única solución que se me ocurre es, como dices, mantener una lista de llamadas / devoluciones de llamadas pendientes. O anidar la próxima llamada en la devolución de llamada anterior, pero se siente un poco desordenado.