Javascript 循环中的多模块ajax调用返回的顺序与调用的顺序不同
下面的代码从数据库中获取列表,然后为每个列表项请求更多数据,并为每个项绘制谷歌图表。除了ajax对更多数据的调用没有以与原始列表相同的顺序返回之外,其他一切都正常工作。根据调用方式的不同,列表有不同的计数,但绘制的图表超过20个。数据库很大,这可能是ajax请求没有按相同顺序返回的主要原因 如何修改代码以在前一个ajax调用返回之前停止另一个ajax调用Javascript 循环中的多模块ajax调用返回的顺序与调用的顺序不同,javascript,jquery,ajax,Javascript,Jquery,Ajax,下面的代码从数据库中获取列表,然后为每个列表项请求更多数据,并为每个项绘制谷歌图表。除了ajax对更多数据的调用没有以与原始列表相同的顺序返回之外,其他一切都正常工作。根据调用方式的不同,列表有不同的计数,但绘制的图表超过20个。数据库很大,这可能是ajax请求没有按相同顺序返回的主要原因 如何修改代码以在前一个ajax调用返回之前停止另一个ajax调用 function getList(fueltype, Date1) { document.getElementById('charts
function getList(fueltype, Date1) {
document.getElementById('charts').innerHTML = "";
var sd = new Date(Date1);
var y = sd.getFullYear();
var m = sd.getMonth();
var d = sd.getDate();
var ed = new Date(y,m,d+1,0,0,0,0);
var startDate = formatDate(sd);
var fuelsearch = '';
var dbFile = '';
var endDate = formatDate(ed);
switch(fueltype){
case 'Coal':
fuelsearch = 'COAL';
dbFile = 'getJSONdata.php';
break;
case 'CCGT':
fuelsearch = 'CCGT';
dbFile = 'getJSONdata.php';
break;
case 'Nuclear':
fuelsearch = 'NUCLEAR';
dbFile = 'getJSONdata.php';
break;
case 'OCGT':
fuelsearch = 'OCGT';
dbFile = 'getJSONdata.php';
break;
case 'Other':
fuelsearch = 'OTHER';
dbFile = 'getJSONdata.php';
break;
case 'Pump Storage':
fuelsearch = 'PS';
dbFile = 'getJSONdataWithMIL.php';
break;
case 'Wind':
fuelsearch = 'WIND';
dbFile = 'getJSONdata.php';
break;
case 'Non Pump Storage Hydro':
fuelsearch = 'NPSHYD';
dbFile = 'getJSONdata.php';
break;
default:
}
$.ajax({
url: "getBmuList.php",
dataType: 'json',
data: {
fuel: fuelsearch
}
}).done(function (listData) {
// draw chart for each id
listData.forEach(function (itemId) {
//console.log(itemId);
drawChart(itemId,startDate,endDate,dbFile);
});
}).fail(function (jq, text, errMsg) {
console.log(text + ': ' + errMsg);
});
}
// This function takes a bmu and gets data in JSON format and draws a google chart
function drawChart(itemId,startDate,endDate,dbFile) {
var bmu = itemId.itemID;
console.log(bmu);
$.ajax({
url: dbFile,
dataType: 'json',
data: {
Id: bmu,
date1: startDate,
date2: endDate
}
}).done(function (jsonData) {
console.log(bmu);
var sd = new Date(startDate);
var y = sd.getFullYear();
var m = sd.getMonth();
var d = sd.getDate();
//console.log(sd);
//console.log(new Date(y,m,d,0,0,0));
var data = new google.visualization.DataTable(jsonData);
var options = {
title: bmu,
width: 495,
height: 300,
series: {
0: { lineWidth: 1, pointSize: 1.1 },
1: { lineWidth: 1, pointSize: 1.1},
2: { lineWidth: 1, pointSize: 1.1},
3: { lineWidth: 1, pointSize: 1.1}},
hAxis: {
textStyle:{fontSize: 10},
format: 'HH:mm',
minValue: new Date(y,m,d,0,0,0),
maxValue: new Date(y,m,d+1,0,0,0),
viewWindow:{
min: new Date(y,m,d,0,0,0),
max: new Date(y,m,d+1,0,0,0)
},
},
vAxis: {
textStyle:{fontSize: 10},
},
chartArea: {backgroundColor: '#fffff0'},
};
// create new div for chart
var div = document.createElement("div");
div.style.width = "500px";
div.style.height = "330px";
div.style.float = "left";
div.style.border = "thin solid #DCDCDC";
div.id = itemId.itemID + "_div";
container = document.getElementById('charts').appendChild(div);
var chart = new google.visualization.ScatterChart(container);
chart.draw(data, options);
//google.visualization.events.addListener(chart, 'click', selectHandler);
}).fail(function (jq, text, errMsg) {
console.log(text + ': ' + errMsg);
});
}
您只能通过在每个回调中一个接一个地调用ajax来实现这一点 或
在ajax调用中添加async:false属性将
async:false
添加到ajax
调用中
从某种程度上说
请不要因为不推荐使用async:false
。您将在浏览器中收到一些警告。如果你不想要这些警告,我建议你重新评估你的方法
而且,
跨域请求和数据类型:“jsonp”
请求不支持同步操作
在你的情况下,看起来是这样的
$.ajax({
url: dbFile,
dataType: 'json',
data: {
Id: bmu,
date1: startDate,
date2: endDate },
async: false
}).done(function (jsonData) {
// Do Stuff
});
Ajax本质上是异步的,所以所有请求都是按顺序触发的,但响应一回来就会被处理 jQuery
$.ajax()
函数有一个异步选项,您可以将其设置为false,使其成为同步的(返回数据而不是传递给success回调,或者在发生错误时抛出数据而不是调用error one)
但是这个选项已经被弃用,很快就会被删除,所以依赖它不是一个好主意
因此,您需要使用一些真正的异步模式。而且,在我看来,承诺是最好的(它们可能在非常旧的浏览器中不受支持,但如果您需要的话,可以使用polyfill来支持它们)
这就是说,您的drawChart()
函数的模式不太好,它不做只做它的名字所说的事情(绘制图表),而是异步地请求数据,因此将这些行为分开是一个非常好的想法
当然,您可以简单地让它返回一个承诺,并以瀑布式样式链接所有调用,以便每个请求+绘图在上一次完成后开始。但是如果(我认为可以理解)这些请求中没有一个会改变其他请求的结果(您只是要求呈现顺序)唯一的区别是你的代码比实际需要的要慢得多
…因此,新的drawChart()
函数将只是您在其内部$.ajax()调用中作为.done()
回调提供的匿名函数
下一步是修改.done()
调用$.ajax()
函数内部的.done()
回调,方法如下:
$.ajax({
...
}).done(function(listData){
Promise.all(
listData.map(function(itemId) {
return new Promise(function(resolve, reject) {
// Code removed from your original drawChart() function
var bmu = itemId.itemID;
console.log(bmu);
$.ajax({
...
})
.done(resolve)
.fail(reject);
});
})
)
.then(function(listDataArr){
// Here all (concurrent) requests finished.
// ...and their results are in right order in listDataArr.
listDataArr.map(drawChart);
})
.catch(reject);
}).fail(...);
希望能有帮助
这并不是最好的方法,但我试着尽可能地与您的代码相似,以便更容易理解所需的最小更改
编辑:另一方面,我发现您可能不需要精确地按顺序渲染它们,而且即使是按随机顺序渲染,也可以将它们按该顺序放置
如果这是真的,那么您就有了另一个更简单的策略,它根本不需要使用承诺,而且,除了需要对代码进行更少的更改之外,它的速度要快得多,而且在我看来,它提供了更好的用户体验:
它包括在开始请求和呈现数据之前,为每个图表创建并放置容器(同步)
例如:
var charts = $("#charts");
var containers = listData.map(function(itemId){
return $("<div></div")
.addClass(itemId+"_div")
.appendTo(charts);
});
// Rendering process here.
…甚至,使用containers数组(或稍微重写下面的代码以生成“itemId:container”对象)会更好.但我让你自己选择。你应该看看如何在上一个ajax调用返回之前将我的代码更改为停止
另一个ajax调用,你的意思是开始
还是停止
?异步调用不是同步的,因此如果顺序比你需要重新考虑你的方法更重要。为什么否决?我认为这是一个有用的方法回答不仅是OP,还有其他正在寻找答案的人。如果不投反对票,不留下理由,这是没有帮助的。给你一票赞成票。我们一直在努力解决这个问题,但一直在努力从listDataArr.map(drawChart)将图表数据获取到drawChart函数;我需要将json数据放入新函数DrawChart的DataTable中。我没有测试任何东西:我只是试图展示一些指导原则来帮助您解决问题。它可能包含我自己的错误,或者您也可能需要修复其他问题。如果您继续在这方面苦苦挣扎,请编辑您的问题,添加更新的方法,以便我们能够分析正在发生的事情……另一方面,我只是随意地编辑了我的回答,添加了一个“编辑”部分,可能不那么礼貌,但更简单(这意味着它需要对您原来的解释进行更少的更改)和(在我的建议中)更好的用户体验,因为它会继续尽可能快地呈现内容,但位置正确。也许你会对尝试它感兴趣……另外,回答你的评论,listdatarr.map(drawChart)
callsdrawChart()
对于每个listDataArr项,将该项作为第一个参数传递,并将其在数组中的位置作为第二个参数传递(即使是函数也无法预料)。请记住,在我的建议中,drawChart()
是您在原始drawChart()中调用的.done()
回调函数
只需要一个参数的函数。另外请注意,如果您需要执行其他操作,您始终可以调用另一个函数(即使是在InLine和/或unnamed中),该函数最后在其内部调用drawChart()
。啊!
var charts = $("#charts");
var containers = listData.map(function(itemId){
return $("<div></div")
.addClass(itemId+"_div")
.appendTo(charts);
});
// Rendering process here.
var div = $("div."+itemId+".div);