0

I'm building a basic hangman game. I have created one class called Hangman and it only has two methods:fetchWord and displayChoices. The fetchWord method takes an url parameter and fetches the word from that specific source. The displayChoices method does what the name says. First, create a list of radio buttons which contains the corresponding letters and display them on the webpage. The only problem with my code is the fetchWord method. Here is the code -- I will explain the issue later.

<body>
<script>
class Hangman {
  fetchWord(url) {
    fetch(url).then(data=>data.json().then(word=>this.word=word[0]));//fetches the word from a random source.
  }
//Create the required elements and put them on the page.
  displayChoices() {
    const choices = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
    choices.forEach(function(choice) {
      const label = document.createElement('label');
      label.innerText=choice;
      label.id=choice;
      const element = document.createElement('input');
      element.type='radio';
        element.value=choice;
      element.id=choice;
      label.appendChild(element);
        document.body.appendChild(label);
    });
  }
}
const a = new Hangman();
a.fetchWord(' https://random-word-api.herokuapp.com/word ');
a.displayChoices();
document.write(a.word);
</script>
</body>

When you run the code, the radio buttons populate. This is exactly what I want. However, on the bottom of the page, it shows undefined. I think this is an async/await problem, because when I added setTimeout and wrapped the document.write(a.word) in it, it works totally fine. I have written my thought process below in case you want to see it. 

First, the class is created .Next, I run the fetchWord method which adds it to the stack.It encounters the fetch API, so it puts it into the browser's call stack space.Now, the fetch API returns the promise, and it was popped out of the stack.The then function is added to the stack -- it runs in the browser's API call stack.When it's done, it puts the callback into the microtask queue.Next, the fetchWord function finally returns and the call stack is empty.Since the call stack is empty, the event loop checks if there are any microtasks that it needs to do. As mentioned earlier, it does. So it runs the callback which was put onto the stack by the then method of Promise.Since this function runs another then, this callback is pushed into the microtask queue.When the callback exits, it is popped out of the stack. Since microtask queues have a higher role than any of the macrotask queues, the event loop keeps running any pending microtasks. As a result, the callback put onto the microtask queue is pushed onto the stack and executed. Once it is done executing, it is popped out of the stack. Next, it runs the displayChoices method. After the displayChoices returns, the document.write is run. Since the callback pushed onto the microtask queue is executed before the document.write, the result (a.word) should be available. However, it is not. How should I construct my code so that it produces the expected result? 

The problems have been described in the post.

Toby Harnish
  • 90
  • 1
  • 9
  • but in case you can't figure it out from the answers to the above question, the easiest way to get what you want is a) add a `return` to `fetchWord`, so it returns the Promise that `fetch` does, b) put `await` before your call to `a.fetchWord()`. (You'll need to either be using a recent browser which allows "top-level `await`", or if not then just wrap this in an `async` function and then call that normally.) – Robin Zigmond Jan 26 '23 at 22:12
  • You say this is an `async/await` problem, but you never use them. If you did, you wouldn't have the problem. – Barmar Jan 26 '23 at 22:19
  • @MartinMeeser your comment is misleading. `DOMContentLoaded` and other events have nothing to do with this, and the code shown is plenty for those who understand about JS and how to handle asynchronous code to understand what is wrong and how to fix it. – Robin Zigmond Jan 26 '23 at 22:19
  • @Barmar, Isn't Promise asynchronous itself? – Toby Harnish Jan 27 '23 at 00:12
  • `fetch()` is asynchronous, but you never use `await` so you don't wait for it. – Barmar Jan 27 '23 at 00:14
  • @Barmar, I think your statement is quite contradicting to the definition of async/await itself. The reason why async/await syntax was introduced in recent JavaScript version is to create a more "synchronous" style of writing asynchronous code. This means that even though I have not used async/await in this case, both should do the same thing. In fact, async/await is just a "syntatic sugar" over promises--with a purpose of creating a much understandable environment within the writing style of asynchronous operations. – Toby Harnish Jan 27 '23 at 00:41
  • `await` is syntactic sugar for using `.then()`. Since you call `a.displayChoices()` without `await` or `.then()`, it doesn't wait for `a.fetchWord()` to finish fetching. – Barmar Jan 27 '23 at 00:44

0 Answers0