Me parece que a veces es necesario recorrer algunos de la colección y hacer una llamada ajax para cada elemento. Quiero que cada llamada a volver antes de pasar al siguiente elemento para que no arruine el servidor con peticiones – que a menudo conduce a otros problemas. Y no quiero establecer async a false y congela el navegador.

Normalmente, esto implica la configuración de algún tipo de iterador contexto que le paso a través de cada éxito de devolución de llamada. Creo que debe haber un limpiador de manera más sencilla?

¿Alguien tiene un diseño inteligente patrón de cómo prolijamente trabajo a través de una colección de hacer llamadas ajax para cada elemento?

9 Comentarios

  1. 111

    jQuery 1.5+

    He desarrollado un $.ajaxQueue() plugin que utiliza el $.Diferido, .queue(), y $.ajax() también para pasar un promesa que se resuelve cuando la solicitud completa.

    /*
    * 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ás usando jQuery 1.4, se puede utilizar la cola de animación sobre un objeto vacío para crear su propia «cola» para sus peticiones ajax para los elementos.

    Usted puede incluso este factor en su propio $.ajax() de reemplazo. Este plugin $.ajaxQueue() utiliza el estándar de ‘fx’ cola para jQuery, que se iniciará de forma automática el primer elemento añadido si la cola no está ya en ejecución.

    (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 De

    Por lo tanto, tenemos un <ul id="items"> que tiene algunas <li> que queremos copiar (usando ajax!) a la <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 }));
            }
        });
    });

    jsfiddle demostraciónLa versión 1.4

    • Si uno enviar más de una solicitud, no la oldComplete ser sobrescrito?
    • No, javascript maneja las variables en una función basada en ámbito… oldComplete es diferente para cada llamada a $.ajaxQueue()
    • ¿Qué pasa si el ajax url depende de la devolución de los datos de la anterior llamada de ajax? Alguna idea de cómo hacer que funcione?
    • Echa un vistazo diferido .pipe() el último ejemplo en la documentación que cubre el escenario
    • Este es un gran trabajo para mí. Una pregunta @gnarf, ¿estaría en lo correcto al asumir que el método de anulación es eliminar peticiones ajax de la cola? Si es así, ¿cómo se llama? Traté de $.ajaxQueue.abort();
    • Es devuelto promesa. Llame .abort() en la solicitud, la misma cosa que usted fije la .done demasiado
    • hola excelente código de gracias por la publicación, me podría decir cómo anexar la respuesta a la li de la cada llamada, en lugar de por separado, en un div, yo soy el procesamiento de los archivos de cada uno de mis li etiquetas tiene un elemento de datos, y quiero agregar que la respuesta a la li contiene el nombre del archivo, espero que esto tenga sentido, quizá debería escribir una nueva pregunta si no es una respuesta simple
    • ok resuelto poner var $this = $(this) y, a continuación, se hace referencia $esta en el éxito de la función 🙂
    • Cualquier forma de saber la cola está totalmente vacío/completa y desencadenar una acción?

  2. 13

    Una rápida y pequeña solución utilizando diferido promesas. Aunque este utiliza jQuery $.Deferred, cualquier otro que debe de hacer.

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

    De uso, llamar a la creación de 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');
    });

    Ver el ejemplo con un lado-por-lado la comparación de peticiones asíncronas.

  3. 3

    Puede envolver todos los que la complejidad de una función para realizar una simple llamada que se parece a esto:

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

    A continuación es un boceto (ejemplo de trabajo, a excepción de la llamada ajax). Esto puede ser modificado para utilizar una cola como la estructura 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
         }
      };
  4. 3

    Idealmente, una corutina con múltiples puntos de entrada para cada llamada del servidor puede llamar al mismo corutina serán ordenadas. Maldita sea, esto va a ser implementado en Javascript 1.7.

    Permítanme tratar de 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);
  5. 2

    Sí, mientras que las otras respuestas se el trabajo, son un montón de código y desordenado. Frame.js fue diseñado con elegancia frente a esta situación. https://github.com/bishopZ/Frame.js

    Por ejemplo, esto hará que la mayoría de los navegadores para colgar:

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

    Mientras 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 del Marco permite la cascada de la respuesta de los objetos y tratar con ellos todo después de toda la serie de petición AJAX han completado (si quieres):

    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()
  6. 2

    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 es posible también usar el nativo de la promesa de apoyo introdujo en ES6. Usted puede envolver la llamada ajax en una promesa y volver 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 llamar a la función de forma recursiva, desde donde se tiene 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;
        }
    }
  7. 1

    Yo uso http://developer.yahoo.com/yui/3/io/#queue para conseguir esa funcionalidad.

    La única solución que se me ocurre es, como dice usted, el mantenimiento de una lista de espera de llamadas /devoluciones de llamada. O anidación de la próxima convocatoria en la anterior de devolución de llamada, pero que se siente un poco desordenado.

  8. 1

    Puede lograr lo mismo con 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

  9. 1

    Yo sugeriría un poco más sofisticado que se puede reutilizar para diferentes casos.

    La estoy usando por ejemplo cuando tengo que frenar una secuencia de llamada cuando el usuario está escribiendo en el editor de texto.

    Pero estoy seguro de que también debe trabajar en la iteración de la colección. En este caso se puede poner en cola las solicitudes y podemos enviarle una sola llamada de AJAX en lugar de 12.

    queueing = {
        callTimeout:                 undefined,
        callTimeoutDelayTime:        1000,
        callTimeoutMaxQueueSize:     12,
        callTimeoutCurrentQueueSize: 0,
    
        queueCall: function (theCall) {
            clearTimeout(this.callTimeout);
    
            if (this.callTimeoutCurrentQueueSize >= this.callTimeoutMaxQueueSize) {
                theCall();
                this.callTimeoutCurrentQueueSize = 0;
            } else {
                var _self = this;
    
                this.callTimeout = setTimeout(function () {
                    theCall();
                    _self.callTimeoutCurrentQueueSize = 0;
                }, this.callTimeoutDelayTime);
            }
    
            this.callTimeoutCurrentQueueSize++;
        }
    }

Dejar respuesta

Please enter your comment!
Please enter your name here