A dynamically content creating function using JSON is returning undefined to an outside function call in javascript

Viewed 57

I wanted to make a TO-DO list app for an assignment using https://jsonplaceholder.typicode.com/todos. The code snippet is shown below.

function generateList() {
  var xhttp = new XMLHttpRequest();

  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var response = JSON.parse(this.responseText);
      var output = "";
      for (let i = 0; i < response.length; i++) {
        // console.log(response[i].title);
        if (response[i].completed == true) {
          output += `<tr id='row${response[i].id}'>`;
          output += `<td><input type="checkbox" id="task${response[i].id}" checked disabled></td>`;
          output += `<td>${response[i].title}</td>`;
        } else {
          output += `<tr id='row${response[i].id}'>`;
          output += `<td><input type="checkbox" id="task${response[i].id}"></td>`;
          output += `<td>${response[i].title}</td>`;
        }
        output += "</tr>";
      }
      // document.querySelector("tbody").innerHTML = output;
      return output;
    }
  };

  xhttp.open("GET", "https://jsonplaceholder.typicode.com/todos", true);
  xhttp.send();
}

let temp = generateList();
document.querySelector("tbody").innerHTML = temp;
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <h1>Success</h1>
  <table>
    <tbody>

    </tbody>
  </table>
  <script src="./json.js"></script>
</body>

</html>

The problem I am facing is that, the table is not displaying. Instead it is showing undefined. When I used the innerHTML method inside the function instead of return statement (I have commented that statement above the return statement), it worked perfectly as I wanted. Can anyone tell me, why am I facing this error?

1 Answers

That's because xhttp.onreadystatechange executes asynchronously and generateList() immediately returns undefined for this call:

let temp = generateList(); //this just returns undefined
document.querySelector("tbody").innerHTML = temp;

In the above call temp will be undefined since the function (generateList) didn't return anything (falsy). And then xhttp.onreadystatechange goes ahead with its usual asynchronous call, fetches that data and manipulates the output. So you can call another function after it's done with manipulating the output

Note that due to the above reason your return output; doesn't actually do anything.

So, this is what you can do:

xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var response = JSON.parse(this.responseText);
      var output = "";
      for (let i = 0; i < response.length; i++) {
        ....
        ....
        output += '...'
      }
      callFunction(output) //invoke a function with this output
      // document.querySelector("tbody").innerHTML = output;
      return output; //NOT needed
    }

Now you can get the output in this

callFunction(output){
  //here you can do anything with the output. for e.g
  document.querySelector("tbody").innerHTML = output;

}

You can also go ahead with publisher and subscriber architecture where after the response is received, an event will be dispatched along with output and there will be a subscriber 'listening' to it which will consume this output.

Related