Why can’t I access my JavaScript array by index outside of the d3 then function?

Tags: , , ,



The ultimate goal is to create a d3 stacked diverging bar chart. The votesData object is created successfully from the data, but I can only access the data within the d3 then function even though the array is created outside of it?

Code:

var votesData = [];

//access votes route
d3.json("/votes").then(function (data) {
    
    //loop through objects in route
    data.forEach(function (d) {

        //convert data to numeric
        demYes = +d.democratic.yes
        demNo = ~d.democratic.no //makes negative
        repYes = +d.republican.yes
        repNo = ~d.republican.no
        indYes = +d.independent.yes
        indNo = ~d.independent.no

        //append var with desired data
        votesData.push( 
            {"name": d.bill.bill_id,
            "question": d.question,
            "description": d.description,
            "votes": [
                {"category": "Democratic Yes",
                "value": demYes },
                {"category": "Democratic No",
                "value": demNo},
                {"category": "Republican Yes",
                "value": repYes},
                {"category": "Repulican No",
                "value": repNo},
                {"category": "Independent Yes",
                "value": indYes},
                {"category": "Independent No",
                "value": indNo}
            ]
        })
    });
    console.log(votesData);
    console.log(votesData[0]);


});
console.log(votesData);
console.log(votesData[0]);

The first set of logs return the array and the first object as expected but the second set of logs (outside the d3 then) return the votesData object, but when trying to access the first index of the object it returns “undefined”. Why? And how would I iterate through that object outside of the d3 then function? Or should I just append everything needed in the svg area within that function?

Answer

The logic inside the then block is asynchronous. What that means it’s scheduled to be executed when the thread has time, and the promise to read a /votes file has resolved. That means that the code is actually executed in the following order:

var votesData = [];
console.log(votesData);
console.log(votesData[0]);

//access votes route
d3.json("/votes").then(function (data) {
    ...
});

That is why it returns undefined, it hasn’t been executed yet! The common workaround is to move all your logic inside the .then() block, or, if you’re using ES2017 you can use async/await.

var votesData = [];

//access votes route
const data = await d3.json("/votes");

//loop through objects in route
data.forEach(function (d) {

    //convert data to numeric
    demYes = +d.democratic.yes
    ...
}

console.log(votesData);
console.log(votesData[0]);

What this compiles to under water is equivalent to the .then() solution, it’s just meant to make your coding experience easier.



Source: stackoverflow