Reputation: 1457
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
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