0

I'm currently trying to use CanvasJS to render multiple charts on my page, and using AJAX to get the data for the tables. I'm using a loop on the AJAX to get the data values, and that's working fine atm, but I'm having problems on getting the value of a variable outside the AJAX.

This is the part I'm having problems with:

for(var i = 0; i < nMeses; i++) {
        var mes = dtIni.addMonths(i);
        var ano = mes.getFullYear();
        mes = mes.getMonth()+1;
        mes = (mes<10 ? '0' : '') + mes;
        var res = ano + mes;
        console.log(res);
        $.ajax({
          url: "getMesGrafico.php",
          type: "GET",
          dataType: "json",
          data: {"m": mes},
          success: function(data) {
            console.log(res);
            var chart = new CanvasJS.Chart("body"+res, {
                animationEnabled: true,
                theme: "light1", // "light1", "light2", "dark1", "dark2"
                title:{
                    text: "Gráfico de contas a pagar do mês"
                },
                data: [{
                    type: "column", //change type to bar, line, area, pie, etc
                    dataPoints: data
                }]
            });
            chart.render();
          },
          error: function(data) {
            console.log("Error");
          }
        });
      }

Inside the ajax>success I'm successfully getting the data I want, but I can't get the right value for 'res'. My console output is currently: 201912 202001 202001 202001, but I'm trying to understand why the first and second values are different since they are in the same loop.

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • 1
    The problem is because you update the value of `res` in every iteration of the loop. The AJAX call then returns at some point *after* the loop has completed and `res` is only ever it's final value. – Rory McCrossan Dec 06 '19 at 14:22
  • Remember that ajax success returns are async, so by building a bunch of ajax calls with a for loop, that `res` variable is going to be willynilly based on when those success actually come back. – IncredibleHat Dec 06 '19 at 14:25
  • 1
    Putting ajax inside a loop is always problematic. You're going to have to design a different way to accomplish what you want. Perhaps with recursive ajax calls (in the success handler, check for a certain condition, if not met, call the ajax again, etc) – Patrick Q Dec 06 '19 at 14:25
  • 1
    The easiest 'hack' for this, would be to pass `res` to the `getMesGrafico.php` and just have `getMesGrafico.php` return that `res` value back in `data` – IncredibleHat Dec 06 '19 at 14:27
  • ^ agreed. Do as much server-side in one request as you can – Patrick Q Dec 06 '19 at 14:29
  • 2
    I think you can overcome this by bulking your requests instead of putting them into a loop – Lhew Dec 06 '19 at 14:43
  • As @Lhew suggests, You should try a bulky operation but if you really need to return the data in chunks you can get the number of iterations you needed an then realize a promise chaining based on that number – stan chacon Dec 06 '19 at 15:21
  • I did what @IncredibleHat suggested and it seems to be working, but is it bad to do so? Also, what would be to bulk the requests? I'd send the ```'nMeses'``` through AJAX and run the loop in the other page? – Rafael Takei Dec 06 '19 at 16:14
  • This can be resolved I think by using the notes in https://stackoverflow.com/a/14220323/125981 scroll down to ES2017+: Promises with async/await – Mark Schultheiss Dec 06 '19 at 19:56
  • @RafaelTakei Since what you need `res` for, is to know which element id to use in `success`... the quick/hack passthru method works here. You are not manipulating `res` in `success`. However if you wanted to alter `res` within `success` for other `success` callbacks (or any other global)... you would need to completely restructure this. – IncredibleHat Dec 07 '19 at 19:03

1 Answers1

0

It's beeen a long time since I've used jquery, however: $.ajax() is an async function, which means it just makes the request and keeps going, it doesn't wait for the response, which is why you need to specify your code inside the "success" callback.

If you want your "res" variable to update synchronously, you should make the async request using a polling function.

Make the request, wait for the response, increase your counter and call the function again.

Here's an example:

let i = 0;
(function poll(){
    var mes = dtIni.addMonths(i);
        var ano = mes.getFullYear();
        mes = mes.getMonth()+1;
        mes = (mes<10 ? '0' : '') + mes;
        var res = ano + mes;
        console.log(res);
        $.ajax({
          url: "getMesGrafico.php",
          type: "GET",
          dataType: "json",
          data: {"m": mes},
          success: function(data) {
            console.log(res);
            var chart = new CanvasJS.Chart("body"+res, {
                animationEnabled: true,
                theme: "light1", // "light1", "light2", "dark1", "dark2"
                title:{
                    text: "Gráfico de contas a pagar do mês"
                },
                data: [{
                    type: "column", //change type to bar, line, area, pie, etc
                    dataPoints: data
                }]
            });
            chart.render();
            //once the request is done, increase "i" and call poll() again
            i++;
            if(i < nMeses)
                poll();
          },
          error: function(data) {
            console.log("Error");
          }
        });
})();
Razvan Tanase
  • 99
  • 1
  • 7