🏠 back to Observable

await d3.json() not async, and .join() not a function ?

Hi,

First of all thank you all. When I read such detailed and generous posts like Overcoming difficulties with JSON data loading (or another mistake) , I really feel like the area of a friendly Internet will never end and that there will always be some oasis with refreshing sources of knowledge.

That been said, I’de like to ask a question relative to the previous post and promise.
I’m trying to port localy Mike’s notebook https://observablehq.com/@d3/disjoint-force-directed-graph. I took 2 unsucessfull approaches:

  • First using:
   d3.json('./data/graph.json')
   .then(function(data) {
       const links = data.links.map(d => Object.create(d));
...
  const link = svg.append("g")
   	  .attr("stroke", "#999")
   	  .attr("stroke-opacity", 0.6)
   	  .selectAll("line")
   	  .data(links)
       /*
         Uncaught (in promise) TypeError: svg.append(...).attr(...).attr(...).selectAll(...).data(...).join is not a function
         at mbnetwork.js:31
         (anonymous)	@	mbnetwork.js:31
         Promise.then (async)		
         (anonymous)	@	mbnetwork.js:5
       ,*/
   	  .join("line")
   	  .attr("stroke-width", d => Math.sqrt(d.value));
...
  • Then using :
    const data = await d3.json('./data/graph.json');
    const links = data.links.map(d => Object.create(d));

/* Uncaught SyntaxError: await is only valid in async function */
...

Each is throwing errors (in comments) that I could not resolve yet.

Please help me understand what is wrong.

For your second point, if the code where you use await is inside a function, you need to replace the keyword function by async function.

1 Like

By “port locally” do you mean that you are trying to port code from Observable to a standalone JS file? Could you share links to gists with the full file(s)?

Here are some guesses based on the snippets you’ve provided. For the first one, .join is not a function makes me think that you might not be using a version of d3 with .join.

For the second one, as @severo has pointed out, you cannot use await at the top level of an ordinary JS file. You can try wrapping your code in an async function and then evaluating it like this:

async function loadData() {
// ...
    const data = await d3.json('./data/graph.json');
    const links = data.links.map(d => Object.create(d));
// ...
}

loadData().then(() => { /* do other stuff */ });
4 Likes

I better understand my first error and the await usage . I was indeed trying to use it at the top level on an ordinary JS file.

For the .join not a function call I also though of a version problem but did not check in detail because I am loading d3.v5 But since you pointed this out, I’m going to check more precisely the subversion differences.

      <!DOCTYPE html>
    <html lang="fr">
    <head>
      <title>mbnetwork</title>
    </head>
    <body>
      <svg id="canvas" height="500"></svg>
      <script type="text/javascript" src="./lib/d3.v5.min.js"></script>
      <script type="text/javascript" src="./js_vis/mbnetwork.js"></script>
    </body>
    </html>
    const height = 600,
          width = 600;
    
    d3.json('./data/graph.json')
        .then(function(data) {
    	const links = data.links.map(d => Object.create(d));
    	const nodes = data.nodes.map(d => Object.create(d));
    
    	const simulation = d3.forceSimulation(nodes)
    	      .force("link", d3.forceLink(links).id(d => d.id))
    	      .force("charge", d3.forceManyBody())
    	      .force("x", d3.forceX())
    	      .force("y", d3.forceY());
    
    	// create the DOM element for the view Box
    	const svg = d3.select("#canvas")
    	      .attr("viewBox", [-width / 2, -height / 2, width, height]);
    
    	const link = svg.append("g")
    	      .attr("stroke", "#999")
    	      .attr("stroke-opacity", 0.6)
    	      .selectAll("line")
    	      .data(links)
    	/*
    	  Uncaught (in promise) TypeError: svg.append(...).attr(...).attr(...).selectAll(...).data(...).join is not a function
    	  at mbnetwork.js:31
    	  (anonymous)	@	mbnetwork.js:31
    	  Promise.then (async)		
    	  (anonymous)	@	mbnetwork.js:5
    	*/
    	      .join("line")
    	      .attr("stroke-width", d => Math.sqrt(d.value));
    
    	const node = svg.append("g")
    	      .attr("stroke", "#fff")
    	      .attr("stroke-width", 1.5)
    	      .selectAll("circle")
    	      .data(nodes)
    	      .join("circle")
    	      .attr("r", 5)
    	      .call(drag(simulation));
    
    	node.append("title")
    	    .text(d => d.id);
    
    	// what is the on tick?
    	simulation.on("tick", () => {
    	    link
    		.attr("x1", d => d.source.x)
    		.attr("y1", d => d.source.y)
    		.attr("x2", d => d.target.x)
    		.attr("y2", d => d.target.y);
    
    	    node
    		.attr("cx", d => d.x)
    		.attr("cy", d => d.y);
    	});
    
    	invalidation.then(() => simulation.stop());
    
        })
    
    
    function drag(simulation) {
        // Qu'est ce que d3.event.active ?
        function dragstrated(d){
    	// si il y a pas d'évenement actif restart simulation ?
    	// d3.event.active the number of currently active drag gestures, drag gesture ?
    	// 0 on start and end
    	if (!d3.event.active) simulation.alphaTarget(.3).restart();
    	// the above is true on start and end drag gestures
    	// set de the element speed ?
    	d.fx = d.x;
    	d.fy = d.y;
        }
    
        function dragended(d) {
    	// how is the diff made between start and end ?
    	if (!d3.event.active) simulation.alphaTarget(0);
    	d.fx = null;
    	d.fy = null;
        }
    
        function dragged(d) {
    	d.fx = d3.event.x;
    	d.fy = d3.event.y;
        }
    
        return d3.drag()
    	.on("start", dragstarted)
    	.on("drag", dragged)
    	.on("end", dragended)
    }

You were right concerning the version. switching the local ref to d3 by the online ref and the join error is gone.

So after a deeper check. I was using d3-v5.0.0 . I upgraded and It’s ok.

small small, I’ll get there… thanks to you