-1

I'm making a simple HTML webpage with some JavaScript. I take information from a JSON file, and with help of JavaScript, I format the text and prepare it to export to a CSV file.

The problem comes when I write the text on the page, and after I try to read it. It gives me an Empty string, so I can't export it.

For testing, I'm just writing the output with console.log, but I'm not getting the response that I expect.

JavaScript

document.addEventListener('DOMContentLoaded', function () {
    convertData();
});

function convertData() {
    fetch('info.json')
    .then(response => response.json())
    .then(data => showData(data))
    .then(print())
    .catch(error => console.error('Error:', error));
}

function showData(data) {
    const container = document.getElementById("container");

    for (var i = 0; i < data["data"].length; i++) {
        var obj = data["data"][i];

        container.innerHTML += "<br>" + obj["field1"] + "; " + obj["field2"] + "; "
    }
}

function print() {
    const container = document.getElementById("container");
    const data = container.innerHTML;
    console.log(data);   
}

HTML

<!DOCTYPE html>
<html>
  <body>
    <div id="container"></div>
  </body>
</html>

When I use container.innerHTML += ".....", it shows me the content correctly, but when I print it, it doesn't show properly.

I can't use TimeOuts, because I don't know how long it can take. It can be 3 seconds on the first time and 40 seconds con the second one.

Maida
  • 235
  • 1
  • 4
  • 12
  • How can i then solve it? I tried adding `async function showData(data) {...}` but nothing changed – Maida Jul 20 '23 at 10:03
  • 1
    The prolem is with `.then(print())` change it to `.then(print)` I'm also working on a more detailed answer – nick zoum Jul 20 '23 at 10:14
  • `.then(print())` — You are *calling* `print` **immediately** and passing its return value to `then()` instead of passing a function to `then()`. See the duplicate which is about the same problem, only with `setTimeout` instead of `then`. – Quentin Jul 20 '23 at 10:17
  • Re edit — *I can't use `TimeOuts`, because I don't know how long it can take. It can be 3 seconds on the first time and 40 seconds con the second one.* — Nobody is suggesting you should use `setTimeout`. The duplicate question explains why you have the problem you do. As my previous comment highlights, `then` (which you are using) and `setTimeout` (which the duplicate uses and makes the same mistake with) work in similar ways. – Quentin Jul 20 '23 at 10:37
  • @Quentin I don't think that's the right duplicate link, the problem seems to be about `.then(print())` vs `.then(() => print())`, so I think we should change the link to something like [https://stackoverflow.com/questions/13286233/pass-a-javascript-function-as-parameter](https://stackoverflow.com/questions/13286233/pass-a-javascript-function-as-parameter) – nick zoum Jul 20 '23 at 10:40
  • @nickzoum — Removing the need for arguments to be passed to the callback makes simpler solutions possible, but solutions in the (slightly) more complex case still work for the simple case. – Quentin Jul 20 '23 at 10:45
  • @Quentin I think there is some miscommunication, I mean the problem is that `.then(print())` is the same as `.then(undefined)`, the solution to the problem is to use either `.then(print)` or `.then(() => print())` – nick zoum Jul 20 '23 at 10:47
  • @nickzoum — Or `.then(function () { print(); })` which is [what the duplicate recommends](https://stackoverflow.com/a/8196237/19068). – Quentin Jul 20 '23 at 10:50
  • @Quentin fair enough, I just mean I thnk it's easier to understand the problem with the other question – nick zoum Jul 20 '23 at 10:51
  • Honestly, reading the awnser of the dupplicate question you gave me, i won't be capable of solving the question. The awnser that is below solved my problem and I understood why its happening. Thanks @nickzoum for your support – Maida Jul 20 '23 at 10:54

1 Answers1

1

The main problem is that you're immediately invoking print. What you're essentially doing is calling print right away (before the fetch request is even sent) and passing the return value of print (undefined) to the then call. What you want to do is pass a function as a paramter so that function is called when the previous promises are resolved.

Just change

function convertData() {
    fetch('info.json')
    .then(response => response.json())
    .then(data => showData(data))
    .then(print())
    .catch(error => console.error('Error:', error));
}

To

function convertData() {
    fetch('info.json')
    .then(response => response.json())
    .then(data => showData(data))
    .then(print)
    .catch(error => console.error('Error:', error));
}

Or

function convertData() {
    fetch('info.json')
    .then(response => response.json())
    .then(data => showData(data))
    .then(() => print())
    .catch(error => console.error('Error:', error));
}

It's also a bit unsafe to use a plain load listener without checking if the respective load event has already triggered, what I like to do is:

if (document.readyState === "complete") convertData();
else addEventListener("load", convertData);

Here is a functional example with a mock fetch:

function mockFetch() {
  const data = {
    data: [{
        field1: "foo",
        field2: "bar"
      },
      {
        field1: "foobar",
        field2: "_"
      }
    ]
  };
  const jsonData = JSON.stringify(data);
  const response = new Response(jsonData);
  return Promise.resolve(response);
}

if (document.readyState === "complete") convertData();
else addEventListener("load", convertData);

function convertData() {
  mockFetch()
    .then(response => response.json())
    .then(showData)
    .then(print)
    .catch(error => console.error(error));
}

function showData(data) {
  const container = document.getElementById("container");

  for (var i = 0; i < data["data"].length; i++) {
    var obj = data["data"][i];

    container.innerHTML += "<br>" + obj["field1"] + "; " + obj["field2"] + "; "
  }
}

function print() {
  const container = document.getElementById("container");
  const data = container.innerHTML;
  console.log(data);
}
<!DOCTYPE html>
<html>

<body>
  <div id="container">
  </div>
</body>

</html>

Some other notes: It's safer to use to let or const instead of var inside for loops link.

You can replace obj["field1"] with obj.field1, you can accesss it normally, you should reserve the ["param"] notation for when the key is not a valid property name (i.e. contains spaces, starts with numbers, contains invalid characters...)

nick zoum
  • 7,216
  • 7
  • 36
  • 80
  • Thanyou, that solved my problem, but I don't really understand why it works when i call it without the parenthesis `()` – Maida Jul 20 '23 at 10:24
  • 1
    @Maida when you call `.then` or `.addEventlistener` or any similar function you want to pass a function as a parameter so the `then` function can call the function when it's ready. In this part `.then(response => response.json())` you are also passing a function – nick zoum Jul 20 '23 at 10:33
  • Oh Okay @nick zoum, i understand. Unanfortunatly for the site, someone closed my question, and this may be usefull for some other people. Thanks, – Maida Jul 20 '23 at 10:34
  • @Maida people can still view the question and the answer, it just means no further answers can be posted. I've also added some extra notes to the answers, that I hope are helpful. – nick zoum Jul 20 '23 at 10:37