几天来我一直在寻找这个问题的答案,但运气不佳。
我正在尝试根据通过对 JSON REST Api 的 ajax 调用收集的数据构建图表。我使用 morris.js 来构建我的图表。图表的数据是通过 3 个嵌套的 ajax 调用收集的。第一个 ajax 调用检索包含任务的列表,成功时循环迭代结果并对每个结果进行 ajax 调用。并在这些调用成功时检索数据以构建图表。
在最后一个成功函数中,for 循环
运行以构建一个包含图表数据的数组。
这些数据被插入图表的参数数组 (morris.js)。
所以 3 个嵌套的 ajax 调用,在第一个调用之后有一个 $.each
循环。
我的问题是每次 ajax 迭代都会构建图表。我一直在尝试将图表函数 (Morris.Donut(params);
) 移出循环,但没有成功。
我的代码:
var arr = [];
var counts = [];
var test = [];
$.ajax({
url: "http://helpdesk.site.com/IT/_vti_bin/ListData.svc/ITHelpdeskRequests?$filter=TaskStatusValue%20eq%20%27Not%20Started%27",
headers: {
'accept': 'application/json;odata=verbose',
'content-type': 'application/json;odata=verbose'
},
success: function(data) {
$.each(data.d.results, function(a, data) {
$.ajax({
url: "http://helpdesk.site.com/IT/_vti_bin/ListData.svc/ITHelpdeskRequests(" + data.RequesterId + ")/CreatedBy",
headers: {
'accept': 'application/json;odata=verbose',
'content-type': 'application/json;odata=verbose'
},
success: function(data2) {
$.ajax({
url: "http://helpdesk.site.com/IT/_vti_bin/ListData.svc/ITHelpdeskRequests(" + data.AssignedToId + ")/AssignedTo",
headers: {
'accept': 'application/json;odata=verbose',
'content-type': 'application/json;odata=verbose'
},
success: function(data3) {
var params = {
element: 'taskchart',
data: [],
colors: ['#b85f28', '#d46125', '#CE4E00']
};
$(".inner").prepend('<p>'+data.Request +' <br>Submitted by: '+data2.d.Name+'<br>Assigned to: '+data3.d.Name+' | Due in: '+data.DueInDays+' day(s)</p>');
var indexOfName = arr.indexOf(data3.d.Name);
if (indexOfName == -1) {
arr.push(data3.d.Name);
counts.push(1);
} else {
counts[indexOfName] ++;
}
for (var i in arr, counts) {
test = {
'label': '' + arr[i] + '',
'value': counts[i]
}
params.data.push(test);
}
Morris.Donut(params);
}
})
}
})
})
}
})
我知道这段代码看起来有点乱,但我似乎无法将其构建得更好。关于在 ajax 调用完成加载数据后如何构建图表的任何建议?
我真的要用头撞墙了。
最佳答案
问题的快速分析
在更仔细地查看您的代码后,您的图表在 $.each
函数中的每次迭代都重新构建的原因是因为您的 Morris.Donut(params);
在循环中最深的嵌套 success
回调中被调用(duh?)。将 Morris.Donut(params);
移到 $.each
函数之外将无法正常工作,因为嵌套的 ajax 调用的异步性质,即调用在 ajax 调用完成之前,$.each
可能会退出并调用 Morris.Donut(params);
。
更简单的备用(服务器端)解决方案
在向您展示一些 javascript 代码之前,我建议如果您可以控制服务器端 API 代码,您应该考虑将所有这些复杂性移到那里;即创建一个接受 TaskStatusValue
输入的新 Web 服务,并返回满足此条件的 IT 请求以及构建图表所需的任何其他信息(请求者姓名、受让人姓名等)。我认为本质上,这就是您的 JS 代码试图做的事情。
正如我在之前的评论中提到的,您的嵌套 AJAX 调用依赖于父 AJAX 调用的结果,这基本上归结为发出同步请求,从而违背了异步调用的目的。
JQuery 解决方案 (TL;DR)
话虽如此,如果您绝对必须在客户端做所有事情,我们将需要传递一个 jQuery.deferred
数组。反对 jQuery.when
函数,以便您的图表仅在所有异步调用完成后绘制。
可以有其他更简单、更好的方法来做到这一点。但这是我的解决方案....
代码重构
将两个嵌套的 AJAX 调用提取到一个单独的函数中,如下所示。此函数将返回 $.ajax
函数返回的 Deferred
对象:
function retrieveITRequestsFrom(url){
return $.ajax(url, {
headers: {
'accept': 'application/json;odata=verbose',
'content-type': 'application/json;odata=verbose'
},
/* etc */
});
}
在您的第一个 success
回调中使用 TaskStatusValue=NotStarted
获取所有 IT 请求后,将您稍后将进行的所有 AJAX(延迟对象)调用添加到一个数组中像这样:
deferreds = []
$.each(data.results, function(){
var requesterURL = 'http://helpdesk.site.com/IT/_vti_bin/ListData.svc/ITHelpdeskRequests(' + this.RequesterId + ')/CreatedBy';
deferreds.push(retrieveITRequestsFrom(requesterURL));
var assigneeURL = 'http://helpdesk.site.com/IT/_vti_bin/ListData.svc/ITHelpdeskRequests(' + this.AssignedToId + ')/AssignedTo';
deferreds.push(retrieveITRequestsFrom(assigneeURL));
});
最后,您可以将这个 Deferred
对象数组传递给 $.when
函数。
$.when.apply($, deferreds)
.then(function(){
$.each(arguments, function(){
console.log(this[0]);
});
})
伪数组arguments
$.each
函数中应包含从所有 ajax 调用中收集的所有结果。您可以在 then
回调中绘制图表。
我用这个 fiddle 做了实验,使用了一些假数据。
关于javascript - 在绘制图表之前在每个循环中编排嵌套的 AJAX 调用 (JQuery),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27144249/