循环中的多个ajax调用的返回顺序与调用的顺序不同



Bellow是从数据库中获取列表的代码,然后为每个列表项请求更多数据,并为每个项绘制谷歌图表。除了ajax调用之外,其他一切都很好,因为更多的数据不会以与原始列表相同的顺序返回。这些列表根据其命名方式有不同的计数,但绘制了二十多张图表。数据库很大,怀疑这是ajax请求未按相同顺序返回的主要原因。

如何在前一次返回之前更改代码以停止另一次ajax调用

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本质上是异步的,因此所有请求都是按顺序触发的,但响应一回来就会被处理。

jQuery$.ajax()函数有一个async选项,您可以将其设置为false,使其成为同步的(返回数据而不是传递给success回调,或者抛出数据而不是在出错时调用error)。

但这个选项已被弃用,很快就会被删除,所以依赖它不是一个好主意。

因此,您需要使用一些真正的异步模式。而且,在我看来,promise是最好的(在非常旧的浏览器中可能不支持它们,但如果你需要的话,有polyfill可以支持它们

也就是说,你的drawChart()函数有一个不太好的模式,它不做只是它的名字所说的(绘制图表),而且异步请求数据,所以分离这些行为是一个非常好的想法。

当然,您可以简单地返回一个promise,并以瀑布样式链接所有调用,这样每个请求+绘图都会在上一次完成后开始。但是,如果(正如我所理解的)这些请求都没有改变其他请求的结果(您只是在请求呈现顺序),唯一的区别将是您的代码将比实际需要的慢很多。

。。。因此,您的新drawChart()函数将只是您在其内部$.ajax()调用中作为.done()回调提供的匿名函数。

下一步将以这种方式修改getList()函数内部$.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(...);

希望能有所帮助。

这不是尽可能好的方法,但我试图将其与您的代码尽可能相似,以更容易理解什么是所需的最小更改。

编辑:另一方面,我发现您可以不完全要求按顺序渲染它们,甚至可以按随机顺序渲染它们。

如果这是真的,那么你有另一种更简单的策略,它根本不需要使用promise,而且除了需要对代码进行更少的更改外,它速度更快,而且在我看来,它提供了更好的用户体验:

它只需为每个图表创建并放置容器(同步),然后开始请求并呈现它们。

例如:

var charts = $("#charts");
var containers = listData.map(function(itemId){
return $("<div></div")
.addClass(itemId+"_div")
.appendTo(charts);
});
// Rendering process here.

您可以使用您的原始实现,唯一的区别是寻址如下div,而不是每次创建新的div:

var div = $("div."+itemId+".div);

…甚至,使用containers数组(或者巧妙地重写下面的代码以生成"itemId:container"对象)会更好。但我让你自己选择。

async: false添加到您的ajax调用中。

从jQuery这样的文档,

$.ajax({
url: 'url'
success: function (result) {
// Do Stuff
},
async: false
});

请不要反对使用async: false。浏览器中会出现一些警告。如果你不想要这些警告,我建议你重新评估你的方法。

此外,跨域请求和dataType: "jsonp"请求不支持同步操作

在你的情况下,它看起来是这样的。

$.ajax({
url: dbFile,
dataType: 'json',
data: {
Id: bmu,
date1: startDate,
date2: endDate },
async: false
}).done(function (jsonData) {
// Do Stuff
});

您只能通过在每个其他回调中一个接一个地调用ajax来实现这一点。

在ajax调用中添加async:false属性

最新更新