d3.csv of https://file.csv returns `property undefined` within script, but OK if defined in separate cell ?

Hi Aaron, the methods you sketched above are not equivalent, and it’s important to understand why.

First, d3.csv() returns a Promise, not an array. So the way JS evaluates this:

   var url_source = d3.csv('https://...')
  
   var input = document.getElementById('exampleInputEmail3')
   input.value = url_source[0].url_orig;

is roughly as follows.

  1. url_source is created as a Promise. It will resolve when the request to the URL succeeds (or fails), however, that will take a fairly long time compared to the evaluation of other code, so JS doesn’t wait for it and moves to the next lines:

  2. input is set to the html element with Id exampleInputEmail3, and then

  3. input.value is set to url_source[0].url_orig. At this point, url_source is still an unresolved Promise, so url_source[0] is undefined. Then .url_orig doesn’t exist, so you get the error that you quoted above.

  4. At some point, url_source resolves, but the above steps have already been performed, so this has no effect.

One thing you can do to get that cell to work in Observable is to throw an await in front:

   var url_source = await d3.csv('https://...')
  
   var input = document.getElementById('exampleInputEmail3')
   input.value = url_source[0].url_orig;

That causes the evaluation of the JS to pause until url_source has resolved. I believe this is what happens implicitly in Observable when you separate the request into different cells:

   var url = external_data
  
   var input = document.getElementById('exampleInputEmail2')
   input.value = url[0].url_orig;
external_data = d3.csv('https:...')

Observable waits for external_data to resolve before evaluating the first cell. Note that separating code into different Observable cells is not equivalent to putting code in different script tags!

In any case, this is not a good solution for a page on the web since what this does is to lock the other code as well as the rendering of the page until the request finishes, which will lead to poor performance and a bad user experience.

A much better approach is to make use of the Promise API as you did in your followup post. The then syntax performs the given function automatically when the request resolves without blocking the rest of the code on the page.

Promises are a bit confusing but very important to the way we get data from other sources on the web, so I recommend spending some time with the docs or looking at some other tutorials (there are many out there!).

2 Likes