56

I am trying to load a GeoJSON file and to draw some graphics using it as a basis with D3 v5.

The problem is that the browser is skipping over everything included inside the d3.json() call. I tried inserting breakpoints to test but the browser skips over them and I cannot figure out why.

Code snippet below.

d3.json("/trip_animate/tripData.geojson", function(data) {

  console.log("It just works");  // This never logs to console.

  //...all the rest
}

The code continues on from the initial console.log(), but I omitted all of it since I suspect the issue is with the d3.json call itself.

altocumulus
  • 21,179
  • 13
  • 61
  • 84
camelCaseCowboy
  • 946
  • 1
  • 10
  • 26

2 Answers2

107

The signature of d3.json has changed from D3 v4 to v5. It has been moved from the now deprecated module d3-request to the new d3-fetch module. As of v5 D3 uses the Fetch API in favor of the older XMLHttpRequest and has in turn adopted the use of Promises to handle those asynchronous requests.

The second argument to d3.json() no longer is the callback handling the request but an optional RequestInit object. d3.json() will now return a Promise you can handle in its .then() method.

Your code thus becomes:

d3.json("/trip_animate/tripData.geojson")
  .then(function(data){
    // Code from your callback goes here...
  });

Error handling of the call has also changed with the introduction of the Fetch API. Versions prior to v5 used the first parameter of the callback passed to d3.json() to handle errors:

d3.json(url, function(error, data) { 
  if (error) throw error;
  // Normal handling beyond this point.
});

Since D3 v5 the promise returned by d3.json() will be rejected if an error is encountered. Hence, vanilla JS methods of handling those rejections can be applied:

  1. Pass a rejection handler as the second argument to .then(onFulfilled, onRejected).

  2. Use .catch(onRejected) to add a rejection handler to the promise.

Applying the second solution your code thus becomes

d3.json("/trip_animate/tripData.geojson")
  .then(function(data) {
    // Code from your callback goes here...
  })
  .catch(function(error) {
    // Do some error handling.
  });
altocumulus
  • 21,179
  • 13
  • 61
  • 84
  • 6
    @Timmmm, the .then method can be provided with an optional second function, one that is called if the promise ends up unfulfilled: `.then(function(data) {}, function(error) {})` – Andrew Reid May 29 '18 at 18:37
  • 6
    @Timmmm Alternatively, you can use `.catch(function(error) {})`, which is mostly equivalent to Andrew's proposal yet more verbally explicit. – altocumulus May 29 '18 at 21:10
  • Assume we can't use the V5 syntax, and must use V4. What are we supposed to do then ? – 3xCh1_23 Jul 29 '21 at 19:51
  • @3xCh1_23 You just use the original code as posted in the question. – altocumulus Jul 29 '21 at 20:19
  • @altocumulus does not work, that was the point... but found a workaround, which is a bit more complicated to share... – 3xCh1_23 Jul 30 '21 at 16:48
-1

Since none of the answers helped, I had to find the solution on my own that works. I am using v4 and have to stick with it. The problem was (in my case) that d3.json worked the first time, but did not work the second or third time (with a HTML dropdown).

The idea is to use the initial function, and then simply to use a second function with

let data = await d3.json("URL");

instead of

d3.json("URL", function(data) {

Therefore, the general pattern becomes:

async function drawWordcloudGraph() {
    let data = await d3.json("URL");
    ...
}


function initialFunction() {
    d3.json("URL", function (data) {
        ...
    });
}

initialFunction();

I have tried several approaches, and only this worked. Not sure if it can be simplified, please test on your own.

3xCh1_23
  • 1,491
  • 1
  • 20
  • 39