Multipule ajax调用循环,而不是以与调用的顺序相同的顺序返回

时间:2017-10-16 14:59:35

标签: javascript jquery ajax

Bellow是从数据库中获取列表然后为每个列表项请求更多数据并为每个项目绘制谷歌图表的代码。 everthing工作正常,除了ajax调用更多的数据没有返回与原始列表相同的顺序。列表具有不同的计数,具体取决于它们的调用方式,但是超过20个图表被绘制。数据库很大,并且怀疑这是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);
  });

}

3 个答案:

答案 0 :(得分:2)

Ajax本质上是异步的,因此所有请求都是按顺序触发的,但是一旦它们返回就会响应。

jQuery $.ajax()函数有一个 async 选项,您可以设置为false使其变为同步(返回数据而不是传递给成功回调或投掷在发生错误时调用错误一个。)

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

所以你需要使用一些真正的异步模式。并且,在我看来,承诺是最好的(它们可能在非常旧的浏览器中不受支持,但是如果需要,可以使用polyfill来支持它们。

话虽这么说,你的drawChart()函数中有一个不太好的模式,因为它没有只是它的名字是什么(绘制一个图表)但异步请求数据,因此将这些行为分开是非常好的想法。

  

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

...因此,您的新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(...);

希望它有所帮助。

它不是尽可能最好的方法但我尝试尽可能地与您的代码相似,以便更容易理解所需的最小更改。

编辑:另一方面,我发现你可能不需要按顺序渲染它们,并且可以按顺序放置它们即使是以随机顺序呈现。

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

只需在开始请求数据并呈现它们之前,为每个图表创建并放置容器(同步)

例如:

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);

...甚至,使用容器数组会更好(或者在代码下面轻轻地重写以生成&#34; itemId:container&#34; object)。但是我把它留给你选择。

答案 1 :(得分:0)

async: false添加到ajax来电。

jQuery docs开始,

$.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
});

答案 2 :(得分:-1)

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

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