Запросы 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");
}
});
});
}
Ответы
Ответ 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'.