dgg32
dgg32

Reputation: 1457

Array in d3.js, console.log can show its contents, but its length is 0

community. I have encountered a very strange d3.js behavior.

In the following html code, the console.log print out the array "projects" and its length in my firefox console. Strangely, the content (A, B, C) is there, but the length part is 0!!

Any help is appreciated, thanks!!

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
                                    
<script src="http://d3js.org/d3.v3.min.js"></script>

<script>(function() {


var event = d3.dispatch("data");

var projects = [];


d3.tsv("example.tsv", type, function(input) {

  event.data(input);
  input.forEach(function (d) {
      if (projects.indexOf(d.project) == -1) {
        projects.push(d.project);
      }
  })
});


graph(event);


function graph(event) {

  event.on("data.graph", function(input) {

    console.log(projects, projects.length);

  });

}



function type(d, i) {
  d.id = i;
  d.project = d.project;
  return d;
}


})()</script>

Here are the example.tsv

project
A
B
C

Edit

OK, big thanks to Lars. I googled d3.tsv and async, found a page in O'Reilly:

http://chimera.labs.oreilly.com/books/1230000000345/ch05.html#_data

Note that d3.csv() is an asynchronous method, meaning that the rest of your code is executed even while JavaScript is simultaneously waiting for the file to finish downloading into the browser. (The same is true of D3’s other functions that load external resources, such as d3.json().)

This can potentially be very confusing, because you—as a reasonable human person—might assume that the CSV file’s data is available, when in fact it hasn’t finished loading yet. A common mistake is to include references to the external data outside of the callback function. Save yourself some headaches and make sure to reference your data only from within the callback function (or from within other functions that you call within the callback function).

Personally, I like to declare a global variable first, then call d3.csv() to load the data. Within the callback function, I copy the data into my global variable (so it’s available to all of my subsequent functions), and finally I call any functions that rely on that data being present.

I admit, this contradicts my personal understanding of program paradigm. But the console.log still confuses me. As Lars said, console.log is not async but it evals the two variables in another order?? Isn't that the definition of async??

Upvotes: 0

Views: 1773

Answers (1)

Lars Kotthoff
Lars Kotthoff

Reputation: 109242

This is because d3.csv is an asynchronous call. That is, the code after the d3.csv block can be run before the call returns and the data is available. This is what happens in Chrome, while Firefox loads the data faster (maybe because of caching?).

The proper way to handle the data retrieved in an asynchronous call is in the callback function entirely, i.e.

d3.tsv("example.tsv", type, function(input) {

  event.data(input);
  input.forEach(function (d) {
      if (projects.indexOf(d.project) == -1) {
        projects.push(d.project);
      }
  });
  graph(event);
});

Upvotes: 2

Related Questions