Рекурсивные AJAX-вызовы-плохая идея?
у меня есть простая функция, чтобы вытащить в массив шаблонов:
function getTemplates(names, done, templates, index) {
if (!index) index = 0;
if (!templates) templates = {};
if (index === names.length) return done(templates);
$.ajax({
url: '/templates/' + names[index] + '.min.html',
success: function (data, status, xhr) {
templates[names[index++]] = data;
return getTemplates(names, done, templates, index);
}
});
}
мне кажется логичным просто перейти от одного к другому, пока все они не будут извлечены, а затем обратный вызов вызывающей функции. Но мне любопытно, если это имеет какие-либо плохие побочные эффекты. Я пока не видел ни одного, но я не хочу идти на производство с этим, не получив сначала некоторое представление о любых потенциальных проблемах.
обновление: С помощью Google и BenjaminGruenbaum я разработал решение:
function getTemplatesAsync(names, done) {
var calls = [];
var templates = {};
names.forEach(function (name, index) {
calls.push(
$.ajax({
url: '/templates/' + names[index] + '.min.html',
success: function (data, status, xhr) {
templates[names[index++]] = data;
}
})
);
});
$.when.apply($, calls).done(function () {
// using "templates" here feels fragile for some reason. Am I wrong?
return done(templates);
});
}
Я использую templates
здесь, потому что мне нужно иметь возможность ссылаться на каждый шаблон по имени, но почему-то он кажется хрупким и ненадежным. Это выглядит безопасным?
3 ответов
ваш обновленный код намного лучше, чем ваш первоначальный, но у него все еще есть несколько проблем, главным из которых является смешивание обещаний и обратных вызовов вместо использования языковых средств (возвращаемое значение) и не использование сопоставлений.
некоторые улучшения могут быть:
- возврат обещания вместо аргумента обратного вызова
- используя
.map
вместо forEach с нажимом. - используя
.then
вместо успешного обратного вызова для избежания двух обработчиков для того же самого и потенциально неопределенного поведения (выполняет ли when сначала? Делаетsuccess:
?)
мы можем сделать что-то вроде:
function getTemplatesAsync(names) {
return $.when.apply(null,names.map(function (name, index) {
return $.get('/templates/' + names[index] + '.min.html');
}).then(function(results){
// map arguments to names
return Array.prototype.reduce.call(arguments, function(obj,cur,idx){
obj[names[idx]] = cur;
return obj;
},{});
});
}
что можно сделать:
getTemplatesAsync(names).then(function(templates){
// access templates here
});
да. Выполнение нескольких вызовов AJAX таким образом-плохая идея, но, возможно, не по той причине, по которой вы думаете.
это приведет к тому, что все ваши вызовы будут выполняться последовательно, а не делать вызовы параллельно и ждать их завершения таким образом.
вам было бы намного лучше использовать обещания чтобы сделать все ваши звонки, а затем ждать их, чтобы закончить, прежде чем продолжить. Это будет выглядеть примерно так:
var promises = [], templates = [], i;
for(i = 0; i < names.length; i++) {
promises.push($.get('/templates/' + names[i] + '.min.html'));
}
$.when.apply($, promises).done(function(responses) {
for(i = 0; i < responses.length; i++) {
templates.push(responses[i][0]);
}
});
хотя он выглядит рекурсивным (и я также использую термин рекурсивный Ajax для этого) технически ваша функция завершает работу до ее повторного вызова, поэтому на самом деле она не рекурсивна... возможно, мы должны назвать их" цепными " Ajax-вызовами, поскольку это просто цепочка асинхронных событий вместе? :)
нет проблем, делая это таким образом, если вы не возражаете против очереди вашего запроса Ajax по одному за раз. Я использовал его таким образом несколько раз, чтобы обеспечить использование полосы пропускания допустимый.
вы должны быть осторожны с крайними случаями, так что он обрабатывает ошибки сервера изящно.
это на самом деле хороший метод для обработки мобильных устройств, которые будут подавляться большим количеством запросов, выходящих сразу.
Я написал плагин, который загружал разделы массивной формы приложения через Ajax и находил устройства, такие как iPad, не могли справиться с более чем несколькими одновременными запросами Ajax. Я закончил использовать рекурсивные / цепные вызовы Ajax для решения проблемы (и бонус не задушить наш сервер тоже):)