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

How to process the results of a series of AJAX requests sequentially?

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

问题当然是对请求的响应并不总是 按顺序得到回答。因此,在阅读 jquery 的 Deferred/promise 界面后,我认为我手头有一个解决方案。但是对于我的生活来说,我无法理解如何推迟请求结果的处理(填充 table),直到处理了先前请求的结果。我在 Whosebug 上找到了很多让我很接近的例子,但我似乎无法将这些点联系起来让它在我的应用程序中正常工作。

首先我尝试使用一组延迟对象,认为我可以使用对它们的索引引用来使一组记录的处理依赖于前一组处理的完成(在 "as requested" 顺序)数据集。但我无法弄清楚如何创建与实际数据处理相关联的 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();
});

然后我发现 "pattern" 使用 then() 链接处理功能, 但该示例不太适合我的确切情况,最终在我的处理数据处理程序中没有引用 http 响应数据。当我 运行 这个时,浏览器记录到控制台,"jsonData undefined" .

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

那么,我怎样才能按顺序强制处理 AJAX 请求的数据 最初提出了哪些要求?提前致谢。

我建议使用原生 Promises over jQuery's deferred objects. They are much more readable, easier to understand, native to the language, and have increasing browser support (with the exception of all IE versions). To account for browser support, use a polyfill like es6-promise and you'll be set. This article 可以很好地解释 promise 的基础知识。

一张一张打印结果,整体比较慢

查看我链接到的那篇文章的标题为“并行性和排序”的部分,因为它确实深入介绍了它的工作原理。本质上,您需要创建一个 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()。这需要一个可迭代的(如数组)承诺,异步运行这些承诺,然后将结果按照传递的顺序保存到一个数组中。如果其中一个承诺失败,整个事情都会失败并给出错误。这听起来正是您所需要的。例如,您可以这样做:

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

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

我不知道有任何 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