0

I'm sorry if this is a duplicate, but I've read a few posts about variable scope and I can't seem to figure this out. Any help is much appreciated.

Basically, I'm just trying to read in a csv and determine how many rows it has and then assign number to a global variable. Here is my code:

<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script type="application/javascript">  
    var maxEpochs;

    setMaxEpochs();
    console.log(maxEpochs);

    function setMaxEpochs() {
        /*
        for (i = 0; i < 2; i++) {
            if ( document.chooseModel.model[i].checked ) {
                modelChoice = document.chooseModel.model[i].value;
                break;
            }
        }
        console.log(modelChoice);
        */


        // set initial value for maxEpochs I DON'T UNDERSTAND WHY THIS DOESN'T WORK
        d3.csv("epochStats.csv", function(d) {
            console.log(d.length);
            maxEpochs = d.length;
            console.log(maxEpochs);
        });
        console.log(maxEpochs);

    }

</script>

NOTE: epochStats.csv just has to be a csv with a few lines in it. That data doesn't matter for this example.

So, when I run this I get the following output in my console:

maxEpochsFail.html:31 undefined
maxEpochsFail.html:12 undefined
maxEpochsFail.html:27 101
maxEpochsFail.html:29 101

The line numbers might not quite match (I have some <head> tags etc at the top), but the point is, the first two console.logs within the function print 100 which is the correct number, but then once I'm outside the function it reverts to undefined.

Any thoughts on this would be much appreciated. I'm kind of banging my head against the wall with it. Thanks, Seth

seth127
  • 2,594
  • 5
  • 30
  • 43
  • I won't close this question (yes, it is a duplicate!) because I feel sorry for your head being banged against the wall! Here is a good reading for you: http://stackoverflow.com/questions/6847697/how-to-return-value-from-an-asynchronous-callback-function . Please read Felix Kling answer to the end, it's worth it. – Gerardo Furtado Mar 07 '17 at 12:30

1 Answers1

1

This is due to the asynchronous nature of javascript. The blocks of code you write do not get evaluated "in order" as you might expect. Basically, anything that uses the d3.csv call HAS to be within that block:

d3.csv("epochStats.csv", function(d) {
        console.log(d.length);
        maxEpochs = d.length;
        console.log(maxEpochs);
        // anything else that uses the value of maxEpochs, or reads the file data, etc....
});
console.log(maxEpochs);  <-- This could be evaluated before the above csv block.
Tim B
  • 3,033
  • 1
  • 23
  • 28
  • I see. So, what is the best way to handle this if I need to have that function run _before_ the other functions (because I need to set `maxEpochs` and I have to load a csv file to set it)? Right now I am just have `onload="setMaxEpochs(); anotherFunction(maxEpochs);"` in the body tag of the page, but I guess that isn't working. – seth127 Mar 07 '17 at 15:11
  • One option would be to have everything within the d3.csv function call, that seems to be quite common with d3. – Tim B Mar 07 '17 at 15:16
  • Ok, I figured out a solution: If a call `onload="setMaxEpochs(); setTimeout(anotherFunction(maxEpochs),200);"` instead, then the 200 ms that it waits before trying the other function are enough time for it to set the `maxEpochs` variable. Someone can let me know if this is a bad idea for any reason, but it seems to work. Thanks to @tim-b for pointing out the asynchronousity being the issue! – seth127 Mar 07 '17 at 20:46