贝娄是从数据库获取列表,然后为每个列表项请求更多数据并为每个项目绘制Google图表的代码。除了对更多数据的ajax调用之外,everything一切正常,返回的数据顺序与原始列表的顺序不同。根据调用方式的不同,列表有不同的计数,但是绘制了二十多个图表。数据库很大,并且怀疑这是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使其变为同步(返回数据而不是传递给成功的回调,或者抛出错误而不是在发生错误时调用错误)。

但是此选项已被弃用,很快将被删除,因此依靠它不是一个好主意。

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

就是说,您的drawChart()函数的模式不是很好,因为它不仅执行其名称所指(绘制图表),而且还异步请求数据,因此将它们分开是一个很好的主意行为。


  当然,您可以简单地使其返回承诺并以瀑布样式链接所有呼叫,以便每个请求+绘图在先前完成之后开始。但是,如果(据我认为理解)这些请求都没有改变其他请求的结果(您只是在要求渲染顺序),唯一的区别就是您的代码将比实际需要的速度慢得多。


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

下一步将以这种方式修改.done()函数内部$.ajax()调用的getList()回调:

$.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,而不是每次都创建一个新的实现:

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


…甚至,最好使用容器数组(或用下面的代码重新编写以产生“ itemId:容器”对象)。但我留给您选择。

关于javascript - 循环中的多个ajax调用未按被调用的相同顺序返回,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46773308/

10-16 14:17