如何按顺序处理一系列AJAX请求的结果?

时间:2015-12-09 18:27:09

标签: javascript jquery ajax jquery-deferred

我需要使用通过一系列AJAX检索的记录来填充HTML表 要求。 AJAX请求都是在while循环中生成的。发出的请求总数由服务器在先前的信息交换中提供的值控制。需要异步处理AJAX请求,同时需要按照最初请求的顺序处理请求的结果。

问题当然是对请求的响应并不总是如此 得到顺序回答。所以,在阅读了jquery的Deferred / promise接口后,我认为我手头有一个解决方案。但是对于我的生活,我无法理解如何推迟处理(填充表格)请求结果,直到先前请求的结果被处理为止。我在StackOverflow上发现了很多让我靠近的例子,但我似乎无法将这些点连接到我的应用程序中。

首先我尝试使用Deferred对象数组,认为我可以使用对它们的索引引用来处理一组记录,这取决于前一组处理的完成(在"按照要求& #34; order)数据集。但我无法弄清楚如何创建与实际数据处理相关的promise / Deferred对象 - 使用.then()。

var deferreds = [];
var recsPerPkt = 20;
var recRqstd = 0;

while(recsRqstd < totalRecsAvailable) {

    console.log("Next record index to request: " + nextNdxRqst)

    // Collect an array of the "promise" objects that $.ajax() returns for each call.
    deferreds.push( $.ajax({
            url: 'eventSummaryData',
            type: 'get',
            cache: false,
            data: {StartNdxNum: nextNdxRqst, NumRecords: recsPerPkt}
        })   // End $.ajax({ url: 'trainSummaryData', ...
    ); // End deferreds.push()

    recsRqstd += recsPerPkt;
    nextNdxRqst = recsRqstd;

    if (deferreds.length > 1)    
    {
        deferreds[deferreds.length - 2].then(        
            function (jsonData) {

                if (jsonData.ok) {

                    // Now display the rows/records included in this packet.
                    displayrRecordsInTable({"rows": jsonData.rows});    
                }
            },
            function(){
                $('#error-msg').text('HTTP error: ' + errorThrown);
            }
        ); 
    } 
} 

$.when.apply(null, deferreds).then( function(){
    console.log("Processing of AJAX'd data complete. ")

    configureTableControls();
});

然后我发现了&#34;模式&#34;使用then()链接处理功能, 但是这个例子并不完全符合我的确切情况,最后在我的process-the-data处理程序中没有引用http响应数据。当我运行它时,浏览器会登录到控制台,&#34; jsonData undefined&#34;

var prevPromise = $.Deferred().resolve();
var recsPerPkt = 20;
var recRqstd = 0;

while(recsRqstd < totalRecsAvailable) {
    prevPromise = prevPromise.then(function(){
        return $.ajax({
            url: 'eventSummaryData',
            type: 'get',
            cache: false,
            data: {StartNdxNum: nextNdxRqst, NumRecords: recsPerPkt}
        }); 
    }).then(function (jsonData) {

            if (jsonData.ok) {
                // Now display the rows/records included in this packet.
                displayTrainSummaryRows({"rows": jsonData.rows});
            }
        },
        function(){
            $('#error-msg').text('HTTP error: ' + errorThrown);
        }
    );

    recsRqstd += recsPerPkt;
    nextNdxRqst = recsRqstd;
}

那么,我如何在序列中强制执行A​​JAX请求数据的处理 请求最初是由哪些人提出的?提前谢谢。

2 个答案:

答案 0 :(得分:2)

我建议使用原生Promises而不是jQuery的延迟对象。它们更具可读性,更易于理解,本机语言本身,并且具有越来越多的浏览器支持(除了所有IE版本)。要考虑浏览器支持,请使用es6-promise之类的polyfill,然后设置。 This article在解释承诺的基础知识方面做得很好。

逐个打印结果,整体较慢

查看我链接到的那篇文章的部分,标题为“并行和排序”,因为它真正深入探讨了它的工作原理。基本上你需要创建一个promise生成器函数,某种映射需要你做多少个ajax请求(在这种情况下只是一个每次上升20的数组),以及一个sequence变量来保存之前的承诺已经完成。

var totalRecsAvailable = 10; //generated elsewhere apparently
var recsPerPkt = 20;
var nextNdxRqst = 0;
var recordRanges = [];
var sequence = Promise.resolve(); //initialize to empty resolved promise

//this generates a promise
function getMyData(startPosition) {
    return new Promise(function(resolve,reject){
        $.ajax({
            type: 'GET',
            dataType: 'json',
            url: 'eventSummaryData',
            data: {StartNdxNum: startPosition, NumRecords: recsPerPkt}
            success: function(response){resolve(response);},
            error: function(response){reject(response);}
        });
    });
}

//build out array to inform our promises what records to pull & in which order
for (var i = 0; i < totalRecsAvailable; i++) {
    recordRanges.push(nextNdxRqst);
    nextNdxRqst += recsPerPkt;
}

//loop through record ranges, chain promises for each one
recordRanges.forEach(function(range) {
    sequence = sequence.then(function() {
        return getMyData(range); // return a new Promise
    }).then(function(data) {
        //do stuff with the data
        addToHtmlTable(data.something);
    }).catch(function(error) {
        //something went wrong
        console.log(error);
    });
});

如该文章所述,使用reduce代替forEach实际上要好一些,但我认为这更清楚发生了什么。

等待所有进程解决,整体更快

要获得稍快的性能,您应该使用Promise.all()。这需要一个可迭代的(如数组)promises,异步运行这些promises,然后按照传递的顺序将结果保存到数组 。如果其中一个承诺失败,整个过程将失败并给出错误。这听起来就像你需要的一样。例如,你可以这样做:

var recsPerPkt = 20;
var nextNdxRqst = 0;
var totalRecsAvailable = 10; //generated elsewhere apparently
var promises = [];

//this generates a promise
function getMyData(startPosition, recordsNumber) {
    return new Promise(function(resolve,reject){
        $.ajax({
            type: 'GET',
            dataType: 'json',
            url: 'eventSummaryData',
            data: {StartNdxNum: startPosition, NumRecords: recordsNumber}
            success: function(response){resolve(response);},
            error: function(response){reject(response);}
        });
    });
}

//create list of promises
for (var i = 0; i < totalRecsAvailable; i++) {
    promises.push(getMyData(nextNdxRqst,recsPerPkt));
    nextNdxRqst += recsPerPkt;
}

//This will run once all async operations have successfully finished
Promise.all(promises).then(
    function(data){
        //everything successful, handle data here
        //data is array of results IN ORDER they were passed
        buildTable(data);
    },
    function(data){
        //something failed, handle error here
        logoutError(data);
    }
);

这应该让你走上正确的道路。

答案 1 :(得分:1)

我不知道任何jQuery函数可以执行您想要的操作,但这里有一个函数processInOrder,在所有先前的异步操作都已解决但仍然允许您访问它们之前,它不会执行回调结果

function processInOrder(arr, cb){
    if( arr.length > 0 ){
        arr[0].then(function(result){
            cb(result);
            processInOrder(arr.slice(1), cb);
        });
    }
}

var deferreds = [];
for(var i=0; i<4; i++){
    deferreds.push( asyncRequest(i) );
}

processInOrder(deferreds, display);

请注意,虽然我不是肯定的,但我相当确定这种递归形式不会为大量请求中断调用堆栈。

这是jsFiddle