Подтвердить что ты не робот

Запросы ajax очереди с использованием jQuery.queue()

Я использую jQuery.queue() в первый раз и не совсем понял. Может ли кто-нибудь указать, что я делаю неправильно?

Глядя в firebug, я все еще вижу, как мои запросы POST срабатывают одновременно, поэтому мне интересно, не вызываю ли я вызов dequeue() в неправильном месте.

Также - как я могу получить длину очереди?

Причина, по которой мне приходится ставить в очередь эти запросы, заключается в том, что она запускается одним нажатием кнопки. И это позволяет пользователю быстро нажимать несколько кнопок.

Пробовал лишить основную структуру моего кода:

$("a.button").click(function(){
   $(this).doAjax(params);
});

// method
doAjax:function(params){ 

   $(document).queue("myQueueName", function(){
     $.ajax({
       type: 'POST',
       url: 'whatever.html',
       params: params,
       success: function(data){
         doStuff;

         $(document).dequeue("myQueueName");
       }
     });
   });

}
4b9b3361

Ответ 1

Проблема здесь в том, что .ajax() запускает асинхронный запуск Ajax-запроса. Это означает, что .ajax() немедленно возвращается, не блокируется. Таким образом, ваша очередь выполняет функции, но они будут срабатывать почти одновременно, как вы описали.

Я не думаю, что .queue() является хорошим местом для запросов ajax, но он больше предназначен для использования fx methods. Вам нужен простой менеджер.

var ajaxManager = (function() {
     var requests = [];

     return {
        addReq:  function(opt) {
            requests.push(opt);
        },
        removeReq:  function(opt) {
            if( $.inArray(opt, requests) > -1 )
                requests.splice($.inArray(opt, requests), 1);
        },
        run: function() {
            var self = this,
                oriSuc;

            if( requests.length ) {
                oriSuc = requests[0].complete;

                requests[0].complete = function() {
                     if( typeof(oriSuc) === 'function' ) oriSuc();
                     requests.shift();
                     self.run.apply(self, []);
                };   

                $.ajax(requests[0]);
            } else {
              self.tid = setTimeout(function() {
                 self.run.apply(self, []);
              }, 1000);
            }
        },
        stop:  function() {
            requests = [];
            clearTimeout(this.tid);
        }
     };
}());

Это далеко от совершенства, я просто хочу продемонстрировать путь. Вышеприведенный пример можно использовать таким образом, как

$(function() {
    ajaxManager.run(); 

    $("a.button").click(function(){
       ajaxManager.addReq({
           type: 'POST',
           url: 'whatever.html',
           data: params,
           success: function(data){
              // do stuff
           }
       });
    });
});

Ответ 2

Мне нужно было сделать что-то подобное, поэтому подумал, что разместил здесь свое решение.

В принципе, у меня есть страница, на которой перечислены проекты на полках, все из которых имеют отличительные критерии. Я хотел погрузить полки один за другим, а не полностью, чтобы получить некоторый контент для пользователя быстрее, на который они могли бы смотреть, в то время как остальные нагрузки.

В основном я сохранил идентификатор каждой полки в массиве JS, который я использую при вызове из PHP.

Затем я создал рекурсивную функцию, которая вызывается первым индексом из массива каждый раз при его вызове и запрашивает полку для всплывшего идентификатора. Как только я получаю ответ от $.get() или $.post() в зависимости от того, что я предпочитаю использовать, я тогда вызываю рекурсивную функцию из обратного вызова.

Здесь выработка кода:

// array of shelf IDs
var shelves = new Array(1,2,3,4);

// the recursive function
function getShelfRecursive() {

    // terminate if array exhausted
    if (shelves.length === 0)
        return;

    // pop top value
    var id = shelves[0];
    shelves.shift();

    // ajax request
    $.get('/get/shelf/' + id, function(){
         // call completed - so start next request
         getShelfRecursive();
    });
}

// fires off the first call
getShelfRecursive();

Ответ 3

Я нашел вышеперечисленные решения сложными, плюс мне нужно было изменить запрос перед отправкой (обновить новый токен данных).

Итак, я поставил это вместе. Источник: https://gist.github.com/2470554

/* 

Allows for ajax requests to be run synchronously in a queue

Usage::

var queue = new $.AjaxQueue();

queue.add({
  url: 'url',
  complete: function() {
    console.log('ajax completed');
  },
  _run: function(req) {
    //special pre-processor to alter the request just before it is finally executed in the queue
    req.url = 'changed_url'
  }
});

*/

$.AjaxQueue = function() {
  this.reqs = [];
  this.requesting = false;
};
$.AjaxQueue.prototype = {
  add: function(req) {
    this.reqs.push(req);
    this.next();
  },
  next: function() {
    if (this.reqs.length == 0)
      return;

    if (this.requesting == true)
      return;

    var req = this.reqs.splice(0, 1)[0];
    var complete = req.complete;
    var self = this;
    if (req._run)
      req._run(req);
    req.complete = function() {
      if (complete)
        complete.apply(this, arguments);
      self.requesting = false;
      self.next();
    }

    this.requesting = true;
    $.ajax(req);
  }
};

Ответ 4

Мне нужно было сделать это для неизвестного числа вызовов ajax. Ответ заключался в том, чтобы вставить каждый в массив, а затем использовать:

$.when.apply($, arrayOfDeferreds).done(function () {
    alert("All done");
});

Ответ 5

Я использую этот очень простой код, чтобы аякс-звонки не переходили друг с другом.

var dopostqueue = $({});
function doPost(string, callback)
{
    dopostqueue.queue(function()
    {
        $.ajax(
        {   
            type: 'POST',
            url: 'thephpfile.php',
            datatype: 'json',
            data: string,
            success:function(result) 
            {
                dopostqueue.dequeue();
                callback(JSON.parse(result));
            }
        })
    });
}

Если вы не хотите, чтобы очередь обрабатывала себя, вы можете просто удалить dequeue из функции и вызвать ее из другой функции. Что касается получения длины очереди, для этого примера это будет:

dopostqueue.queue().length

Ответ 6

вы можете расширить jQuery:

(function($) {
  // 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);

то используйте его как:

$.ajaxQueue({
    url: 'doThisFirst.php',
    async: true,
    success: function (data) {
        //success handler
    },
    error: function (jqXHR,textStatus,errorThrown) {
        //error Handler
    }       
});
$.ajaxQueue({
    url: 'doThisSecond.php',
    async: true,
    success: function (data) {
        //success handler
    },
    error: function (jqXHR,textStatus,errorThrown) {
        //error Handler
    }       
});

конечно, вы можете использовать любые другие параметры $.ajax, такие как тип, данные, contentType, DataType, поскольку мы расширяем $.ajax

Ответ 7

Другая версия jAndy отвечает без таймера.

var ajaxManager = {
    requests: [],
    addReq: function(opt) {
        this.requests.push(opt);

        if (this.requests.length == 1) {
            this.run();
        }
    },
    removeReq: function(opt) {
        if($.inArray(opt, requests) > -1)
            this.requests.splice($.inArray(opt, requests), 1);
    },
    run: function() {
        // original complete callback
        oricomplete = this.requests[0].complete;

        // override complete callback
        var ajxmgr = this;
        ajxmgr.requests[0].complete = function() {
             if (typeof oricomplete === 'function')
                oricomplete();

             ajxmgr.requests.shift();
             if (ajxmgr.requests.length > 0) {
                ajxmgr.run();
             }
        };

        $.ajax(this.requests[0]);
    },
    stop: function() {
        this.requests = [];
    },
}

Для использования:

$(function() {
    $("a.button").click(function(){
       ajaxManager.addReq({
           type: 'POST',
           url: 'whatever.html',
           data: params,
           success: function(data){
              // do stuff
           }
       });
    });
});

Ответ 8

На сайте learn.jquery.com есть хороший пример:

// 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 invoke the next event in the queue
    ajaxOpts.complete = function() {
      // Invoke the original complete if it was there
      if (oldComplete) {
        oldComplete.apply(this, arguments);
      }

      // Run the next query in the queue
      next();
    };

    // Run the query
    $.ajax(ajaxOpts);
  });
};

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

Ответ 9

Я тоже должен был сделать это в решении, которое у меня было, и я обнаружил, что могу сделать это следующим образом:

//A variable for making sure to wait for multiple clicks before emptying.
var waitingTimeout; 

$("a.button").click(function(){
   $(this).doAjax(params);
   clearTimeout(waitingTimeout);
   waitingTimeout = setTimeout(function(){noMoreClicks();},1000);
});

// method
doAjax:function(params){ 

   $(document).queue("myQueueName", function(next){
     $.ajax({
       type: 'POST',
       url: 'whatever.html',
       data: params,
       contentType: "application/json; charset=utf-8",
       dataType: "json",
       success: function(data){
         doStuff;
         next();
       },
       failure: function(data){
         next();
       },
       error: function(data){
         next();
       }
     });
   });

}

function noMoreClicks(){
    $(document).dequeue("myQueueName");
}

используя обратный вызов next(), который передается в функции очереди, вы можете удалить очередную операцию. Таким образом, добавив следующий в обработчики для ajax, вы фактически сделаете аякс-вызовы асинхронными для браузера и потоком рендеринга или рисования браузера, но сделайте их синхронными или сериализованными друг с другом.

Вот очень простой пример. В примере скрипта. Нажмите кнопку один раз и подождите секунду. Вы увидите, что срабатывают тайм-аут и одна операция. Затем нажмите кнопку так быстро, как вы можете (или быстрее, чем на одну секунду), и вы увидите, что все время, когда вы нажимаете кнопку, операции ставятся в очередь, а затем только после ожидания второй они попадают на страницу и исчезают в одном после другой.

Красота заключается в том, что если очередь уже опустошена, любые операции, которые вы добавляете к ней во время ее опустошения, помещаются в конец, а затем обрабатываются только при наступлении времени.

Ответ 10

Вот мое решение, которое я использую для создания очереди запросов для некоторых браузеров. Если что-то случится, я остановлю эту очередь и закончу работу с помощью специального последнего запроса или очистки.

var get_array = ["first", "second", "third"];

var worker = $("<div />"); // to line up requests in queue
$.queuedAjax = function(args){  // add up requests for me       
    worker.queue(
        function(next){
            $.ajax(args).always(next);            
        }
    );
  };

$.queuedSomething = function(){ // add up something special for me
    worker.queue(
        function(next){
            //worker.clearQueue();
            //worker = $("<div />"); //cleanup for next .each
            //maybe another .each           
        }
    );
  };

$.each( get_array , function( key , value ) {
  $.queuedAjax({
    type: 'GET',
    url: '/some.php?get='+value,
    dataType: 'text',
    success: function(sourcecode){

        if (sourcecode.match(/stop your requests, idiot!/)) {   
            worker.clearQueue().queue($.queuedSomething);
            alert(' the server told me to stop. i stopped all but not the last ´$.queuedSomething()´ ');
        }

    }
  });           
}); 
$.queuedSomething();

Ответ 11

У меня была аналогичная проблема,

Если это не тот импорт для вас, что ajax-вызов является асинхронным (даже если это "a" ajax), вы можете просто сказать jQuery, чтобы выполнить ajax-запрос не async. это сработало для меня и не приносит необходимости в более широком обходном пути.

    $.ajax({
       ...
      async: false
    });

Я должен упомянуть, что это не рекомендуется с jQuery 1.8, и он не будет работать с dataType: 'jsonp'.