递归地进行多个AJAX调用是一个坏主意

时间:2014-09-28 16:43:35

标签: javascript jquery ajax recursion promise

我已经完成了这个问题。 Are recursive AJAX calls a bad idea? Justin Niessner提供的答案很好(通过使用承诺)但我的问题有点不同。 我想以递归方式调用10 ajax calls。就像第一次ajax调用完成时一样,我在我的视图中渲染该数据(由ajax调用提供),同时我将调用第二个ajax调用。所以我的代码示例将如下所示:

(function() {
    var downloadAsync = function(url, callback) {
        var httpRequest;
        if (window.XMLHttpRequest) {
            httpRequest = new XMLHttpRequest();
        } else if (window.ActiveXObject) {
            httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
        }
        httpRequest.onreadystatechange = function() {
            if (httpRequest.readyState === 4 && httpRequest.status === 200) {
                callback(httpRequest.responseText);
            }
        }
        httpRequest.open("GET", url, true);
        httpRequest.send();
    }

    function renderData(data) {
        //data render logic          
    }
    downloadAsync("mathmatics.json", function(response) {
        renderData(response);
        downloadAsync("science.json", function(response) {
            renderData(response)
            downloadAsync("english.json", function(response) {
                renderData(response);
                ....................................
            });
        });
    });

})();

我想尽快呈现每个数据。因此,在每次ajax调用成功时,我都在渲染数据。 这是我的问题:

这样做有什么好处吗?我可以在这种情况下使用promises吗? (渲染数据     有必要尽快)。如果是,那么如何以及如何不是进行此类呼叫的最佳方式。

  1. 注意:渲染ajax成功数据是我的首要任务。
  2. 编辑 - 浏览器支持 - 最新的chrome,firefox和IE-11。
  3. 编辑 - 根据评论改进:
  4. var array = ["mathematics.json", "science.json", "english.json"];
    for (var i = 0; i < array.length; i++) {
      downloadAsync(array[i], renderData);
    }
    
    function renderData(data) {
      console.log(data);
        }
    

2 个答案:

答案 0 :(得分:2)

嵌套的Ajax调用工作。但是有一些缺点:

  • 如果你正在进行真正的错误处理,多次嵌套的ajax调用可能会非常混乱,你经常会在嵌套的每个级别重复错误处理。
  • 在某些人看来,深度嵌套会导致代码的可读性低于其他技术
  • 如果嵌套中的任何代码都可以抛出异常,则需要大量的异常处理程序来正确捕获所有异常,并且很难干净地传播错误 - 因为这些是异步回调,你不能只捕获异常一个地方。

请参阅“答案结束时的编辑”,现在问题的意图已经澄清

至于其他方法,使用promises有一些好处。由于您的调用都不依赖于之前的调用集,并且您只想按正确的顺序插入结果,因此您可以通过立即发送所有ajax请求然后序列化结果来显着加快端到端时间回来了。这可以通过手动编码完成,也可以通过promises轻松完成。

你没有说你计划使用什么诺言库(有几个),但这里是使用Bluebird完成的(虽然代码与其他库类似或相同):

(function() {
    // downloadAsync now returns a promise rather than uses a callback
    function downloadAsync(url) {
        return new Promise(function(resolve, reject) {
            var httpRequest = new XMLHttpRequest();
            httpRequest.onreadystatechange = function() {
                if (httpRequest.readyState === 4) {
                    if (httpRequest.status === 200) {
                        resolve(httpRequest.responseText);
                    } else {
                        reject(httpRequest.status);
                    }
                }
            }
            httpRequest.open("GET", url, true);
            httpRequest.send();
        }
    }

    function renderData(data) {
        //data render logic          
    }
    var items = ["mathmatics.json", "science.json", "english.json"];
    var promises = items.map(function(path) {
        return downloadAsync(path);
    });

    // Promise.all collects all the promise results and 
    // calls .then only when all the promises have completed
    Promise.all(promises).then(function(data) {
        data.forEach(renderData)
    }).catch(function(e) {
        // handle errors here
    });

})();

这具有以下性能优势:它一次请求所有数据,但随后按原始请求顺序处理结果。与序列化相比,这缩短了端到端时间,就像使用request-response-request-response-request-response一样。这开始于request-request-request和Promise.all为我们做的工作是将结果按正确的顺序排列,以便我们按照我们要求的顺序处理它们。


如果要尽快显示任何列,您甚至不必等待所有列完成,代码的第二部分可以是:

// note we are passing an index here so you know which column it is in case that isn't
// already specified in the data
function renderData(index, data) {
    //data render logic          
}

// are you sure you want a leading / on only one of these three?
var items = ["mathmatics.json", "science.json", "english.json"];
items.forEach(function(value, index) {
    downloadAsync(value).then(function(data) {
        renderData(index, data);
    });
});

Promise并没有让你受益,因为你可以使用你的回调机制编写类似的结构。


如果没有承诺,您可以使用原始downloadAsync()功能并执行此操作:

downloadAsync("mathmatics.json", renderData);
downloadAsync("science.json", renderData);
downloadAsync("english.json", renderData);

这将并行启动所有三个并在数据到达时立即渲染每个。


或者,如果你有三个以上,你把文件名放在任何长度的数组中,你可以这样做:

["mathmatics.json", "science.json", "english.json"].forEach(function(item) {
   downloadAsync(item, renderData);
});

答案 1 :(得分:0)

由于您正在使用新的浏览器 - 您的生活可以通过承诺简化很多

您正在检查自IE6以来不相关的内容,您可以大大简化您的工作。

以下是:

function downloadAsync(url){
    return new Promise(function(fulfill, reject){
        var xhr = new XMLHttpRequest;
        xhr.open("GET", url);
        xhr.onload = function(){ resolve(xhr.responseText); });
        xhr.onerror = reject;
        xhr.send();
    });
}

不需要检查XMLHttpRequest,不需要状态检查,简单的onload - 注意我们还添加了一个错误,所以我们不会使错误沉默。

现在,让我们一次下载所有三个:

// your pages
var pages = ["mathmatics.json", "science.json", "english.json"]
// your downloads and renders 
var promises = pages.map(downloadAsync).
                    /*map(JSON.parse).*/
                     map(renderData);
Promise.all(promises).then(function(){
    // code here runs when it's all done.
});