如何处理对循环中发出的多个异步请求(AJAX 调用)的响应
How to handle response to several asynchronous request (AJAX calls) made in a loop
我需要从后端收集不同设备的日志,以将其导出到csv文件。问题是设备的数量可能会有所不同。因此,我向后端询问设备数量并循环访问日志请求。
问题是for...
循环的运行速度比我获得对$.post
的响应快,所以循环在我得到响应之前完成。经过一些研究,我可以处理这种行为,但现在我被要求向请求添加数据,我没有参考存储相应的设备。因此,我在外部js文件中添加了需要轮询的设备名称和点,因此我有一个定义的列表来循环。
我尝试使用 for 循环的索引来获取设备名称,但没有奏效,因为循环太快了。现在,我已经创建了一个解决方法,通过定义一个计数器并在另一个变量中推送设备。这感觉并不"干净",应该有一种更好的方法来轮询数据并跟踪它是哪个设备。
到目前为止的代码:
function collectData() {
var outString = "";
var lots of stuff I can pre-fetch
var logs = function (outString, saveCSV) {
var postString;
var devices = [];
var count = 0;
for (i = 1; i <= maxDevice; i++) {
postString = build postString in loop
devices.push(spots[0][i - 1]);
$.post('/path/foobar.db',
postString,
function (data) {
outString += "Spotlist for: " + spots[0][count] + "'n";
count++;
outString += data.replace(/{/g, "").replace(/}/g, "").replace(/:/g, ";").replace(/,/g, "'n").replace(/'"/g, "");
outString += "'n'n";
});
postString = "/path/eventlog.csv?device=" + i;
$.get(postString,
function (data) {
outString += "Event Log: 'n" + data + "'n";
});
postString = "/path/errorlog.csv?device=" + i;
$.get(postString,
function (data) {
outString += "Error Log: 'n" + data + "'n";
});
}
$(document).ajaxStop(function () {
saveCSV(outString, filename);
$(this).unbind('ajaxStop');
});
};
var saveCSV = function (outString, filename) {
var tempString = "data:text/csv;charset=utf-8," + outString;
var encodedUri = encodeURI(tempString);
var a = document.getElementById("dlLink");
if (window.navigator.msSaveOrOpenBlob) {
blobObject = new Blob([outString], {type: 'text/csv;charset=utf-8'});
window.navigator.msSaveBlob(blobObject, filename);
} else
{
a.setAttribute("href", encodedUri);
a.setAttribute("download", filename);
a.click();
}
};
outString = lots of predefined and pre-fetched stuff
outString += "Device data: 'n'n";
logs(outString, saveCSV);
}
我不满意的部分是:
for (i = 1; i <= maxDevice; i++) {
postString = "get = {" + i + ":en:[";
for (j = 0; j < spots[i].length; j++) {
postString += '"' + spots[i][j] + '",';
}
postString = postString.slice(0, -1) + "]}";
devices.push(spots[0][i - 1]);
$.post('/path/foobar.db',
postString,
function (data) {
outString += "Spotlist for: " + spots[0][count] + "'n";
count++;
outString += data.replace(/{/g, "").replace(/}/g, "").replace(/:/g, ";").replace(/,/g, "'n").replace(/'"/g, "");
outString += "'n'n";
});
为了输出我收集斑点的设备,我使用计数器来跟踪设备名称。我有预感这不是最好和"最干净"的方法,所以我想问一下是否有更好的方法来处理异步性(sp?),以收集正确的设备,如果一切都完成,也会触发DL。
由于我的问题似乎不清楚,也许我需要缩小范围。代码有效,但它似乎只是被我修补了,应该有更干净的方法来
A) 处理帖子/获取,因为 CSV 的输出字符串只是以应答请求的方式组合在一起,因此不是设备 1 是 csv 中的第一个,而是第一个。 $(document).ajaxStop
等待一切完成,但不是以正确的顺序完成。
B) 我需要将 for 循环的索引与我轮询数据的设备相关联。我使用了额外的变量,我计算这些变量以通过一个额外的数组。有没有更好的方法?
问题是您需要按顺序运行在获得对 AJAX 调用的响应后调用的方法。
要做到这一点,你必须明白所有jQuery AJAX调用都返回promise。您可以执行以下操作,而不是将代码作为参数传递:
var functionToRunAfterResponse(params) = function(params) {
// do something with params
};
var promise = $.post(/*...*/); // some kind of ajax call
promise.then(functionToRunAfterResponse);
请注意,functionToRunAfterResponse
将接收响应数据作为参数。 即它等效于:
promise.then(function(reponseData) {
functionToRunAfterResponse(responseData);
});
这就是它用于简单呼叫的工作方式。请参阅 $.then 和延期和承诺文档。
如果您必须拨打一堆电话,则必须执行以下操作:
- 存储 AJAX 调用的承诺,例如在数组中
- 检查所有承诺是否兑现(即所有回复是否已到达)
- 按所需顺序运行代码。 即以正确的顺序运行
promise.then
以获得所需的结果。
为此,您可以使用 $.when。
代码结构应该是这样的(伪代码):
var promises = [];
// Make the calls, and store the promises
for(/**/) {
promises.push( $.post or some other kind of ajax call );
}
// Ensure that all the responses have arrived
$.when.apply($, promises) // See below note
.then(function() {
for each promise in promises
promise[i].then(function(reponseData) {
// run the necessary code
});
注意:在这里,您有更深入的解释,但是,基本上,正如$.when
所期望的那样,一系列承诺,如下所示:$.when(promise1, promise2, ...)
并且您有一个数组promises[]
,您必须使用 apply 进行调用,以便数组中的项目作为单独的参数传递。
结语:
1) 考虑到 AJAX 调用可能会失败。在这种情况下,你得到的不是已解决的承诺,而是失败的承诺。你应该检查一下。如果任何承诺失败,$.when
将无法按预期工作:
方法 [when] 将在所有延迟解决后立即解析其主节点"延迟",或者在其中一个"延迟"被拒绝后立即拒绝主节点"延迟"。
2) 为了处理错误,then
有两个参数:
$.when.apply().then(function() { /* success */ }, function() { /* error */ });
3)当您按照我的解释进行调用时,它们都是并行执行的,并且响应将以任何顺序到达。也就是说,不能保证您将按照拨打电话的顺序收到所有响应。它们甚至可能失败,正如我在 1) 中解释的那样,这就是为什么您必须使用 when
并再次按顺序运行
4)使用异步方法很棒,但您有责任检查它们是否失败,并在获得响应后以正确的顺序运行您需要运行的代码。
我无法理解您的问题,但主要问题是异步性,对吧?
尝试异步调用函数(摘要版本):
// Function that manage the data from ajax
var myResponseFunction = function (data) {
$('#response').html('data: ' + JSON.stringify(data));
};
// Ajax function, like your post with one parameter (add all you need)
function myAJAXFunction(callback) {
$.ajax({
type: 'POST',
url: '/echo/json/',
dataType: 'json',
data: {
json: JSON.stringify({
'foo': 'bar'
})
}
}).done(function(response) {
// Done!! Now is time to manage the answer
callback(response);
}).fail(function (jqXHR, textStatus, errorThrown) {
window.console.error('Error ' + textStatus + ': ' + errorThrown);
});
}
// Usually, this function it's inside "document.ready()".
// To avoid the ajax problem we call the function and "data manage function" as parameter.
for (i = 1; i <= maxDevice; i++) {
myAJAXFunction(myResponseFunction);
}
https://jsfiddle.net/erknrio/my1jLfLr/
这个例子是西班牙语的,但在这个答案中,你用英语:)注释了代码。
对不起我的英语:S。
- 调用后不异步Ajax忽略函数
- 在异步AJAX调用完成后,通过变量运行函数
- 将 jQuery UI 日期选择器与异步 AJAX 请求一起使用
- AngularJS:多个异步AJAX调用
- Qt-WebView是否支持包含异步Ajax的网页
- 同步/异步AJAX函数的模式
- 单击链接时执行异步AJAX调用,然后跟随该链接
- Javascript异步AJAX和递归排序
- 记住使用异步 Ajax 获取数据的 Javascript 函数
- 如何将异步 ajax 响应捕获到变量中
- 异步 ajax 调用阻止 UI
- JavaScript 中异步 Ajax 调用的测试结果
- 使用异步/ajax 时保持不变的粘性页脚
- 使用 ko.mapping.fromJS 更新异步 ajax 调用后可观察的淘汰表
- 使用异步 AJAX 或 jQuery 完全重新加载页面
- 而循环与jQuery异步AJAX调用
- Javascript:同步使用异步 AJAX
- JQuery/Ajax:如何将多个 ajaxes 组合成一个具有响应的异步 ajax
- 双异步 AJAX 函数
- jQuery 等到异步 ajax 调用完成