Рекурсивные 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 для решения проблемы (и бонус не задушить наш сервер тоже):)