递归AJAX调用是个坏主意吗?

时间:2014-04-08 14:27:14

标签: jquery ajax recursion promise

我有一个简单的函数来提取模板数组:

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 个答案:

答案 0 :(得分:5)

是。以这种方式进行多个AJAX调用是一个坏主意,但可能不是你想的原因。

这将导致所有调用按顺序执行,而不是并行调用并等待它们以这种方式完成。

使用promises拨打所有电话然后等待所有电话完成再开始之前,你会好得多。它看起来像是:

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]);
   }
});

答案 1 :(得分:3)

虽然它看起来是递归的(我也使用术语递归Ajax )技术上你的函数在再次被调用之前退出,所以实际上并不是递归...也许我们应该调用它们& #34;链&#34; Ajax调用,因为它只是将Async事件链接在一起? :)

如果您不介意一次排队Ajax请求,那么这样做是没有问题的。我用这种方式几次以确保带宽使用是可以接受的。

您需要注意边缘情况,以便正常处理服务器错误。

这实际上是一种处理移动设备的好方法,它会同时阻止大量请求。

我写了一个插件,通过Ajax加载了大量应用程序表单的部分,发现像iPad这样的设备无法应对多个同时发生的Ajax请求。我最后使用递归/链式Ajax调用来解决问题(并且还有不使我们的服务器窒息的奖励):))

答案 2 :(得分:1)

您的更新代码比最初的代码要好得多,但它仍然存在一些问题,主要是混合承诺和回调而不是使用语言功能(返回值)而不使用映射。

一些改进可以是:

  • 返回promise而不是回调参数
  • 使用.map代替forEach with push。
  • 使用.then而不是成功回调来避免同一事物的两个处理程序和可能未指定的行为(何时执行?是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
});