Я нахожу, что иногда должен выполнять итерации некоторого набора и заставлять ajax призвать к каждому элементу. Я хочу, чтобы каждый вызов возвратился прежде, чем переместиться в следующий элемент так, чтобы я не уничтожал сервер с запросами - который часто приводит к другим проблемам. И я не хочу устанавливать асинхронный на ложь и замораживать браузер.
Обычно это включает установку некоторого контекста итератора, что я ступаю через в каждый обратный вызов успеха. Я думаю, что должен быть более чистый более простой путь?
У кого-либо есть умный шаблон разработки для того, как аккуратно работать через набор, делающий ajax призывы к каждому объекту?
Я разработал плагин $. AjaxQueue ()
, который использует $. Deferred
, .queue ( )
и $. Ajax ()
, чтобы также передать обратно обещание , которое выполняется после завершения запроса.
/*
* 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, вы можете использовать очередь анимации для пустого объекта, чтобы создать свою собственную «очередь» для ваших запросов ajax для элементов.
Вы даже можете учесть это в своей собственной $. Ajax ()
замене. Этот плагин $. AjaxQueue ()
использует стандартную очередь fx для jQuery, которая автоматически запускает первый добавленный элемент, если очередь еще не запущена.
(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);
Итак, у нас есть
, в котором есть некоторые
, которые мы хотим скопировать (используя ajax!) В
// 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 }));
}
});
});
В идеале сопрограмма с несколькими точками входа, чтобы каждый обратный вызов с сервера мог вызывать одну и ту же сопрограмму, будет аккуратным. Блин, вот-вот это будет реализовано в Javascript 1.7.
Позвольте мне попробовать использовать закрытие ...
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);
Вы можете обернуть всю эту сложность в функцию, чтобы сделать простой вызов, который выглядит примерно так:
loadSequantially(['/a', '/a/b', 'a/b/c'], function() {alert('all loaded')});
Ниже приведен грубый набросок (рабочий пример, за исключением вызова ajax). Это можно модифицировать, чтобы использовать структуру, подобную очереди, вместо массива
// 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
}
};
Я использую http://developer.yahoo.com/yui/3/io/#queue, чтобы получить эту функциональность.
Единственное решение, которое я могу предложить, это, как вы говорите, ведение списка ожидающих вызовов / обратных вызовов. Или вложение следующего вызова в предыдущий обратный вызов, но это кажется немного беспорядочным.