0

I wrote a function fetching data from the Hacker News API with fetch and .then methods. That worked, but I'd like to learn to use the async / await -method. For some reason, the latter fetch within forEach loop returns undefined. Any idea how to fix this?

Here's the original code:

  componentDidMount() {
    // Get the top 20 post ids
    fetch("https://hacker-news.firebaseio.com/v0/beststories.json")
      .then(res => res.json())
      .then(json => {
        let topIds = json.slice(0, 20)
        this.setState({
          topIds: topIds
        })
        return topIds
      })

      // Get the posts based on the ids
      .then(ids => {
        ids.forEach(id => {
          fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`)
            .then(res => res.json())
            .then(data => {
              let thisPost = data
              this.setState({
                topPosts: [...this.state.topPosts, thisPost]
              })
            })
        })
      })
  }

Here's the new version:

  async componentDidMount() {
    this.getIds()
  }

  // Get the ID's
  async getIds() {
    // Get the top 20 post ids
    const res = await fetch("https://hacker-news.firebaseio.com/v0/beststories.json")

    const json = await res.json()

    const topIds = await json.slice(0, 20)
    this.setState({
      topIds: topIds
    })

    this.getPosts()
  }

  // Get the posts
  async getPosts() {
    const posts = this.state.topIds.forEach(id => {
      fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`)
    })

    const thisPost = await posts.json() // Posts returnes as undefined

    this.setState({
      topPosts: [...this.state.topPosts, thisPost]
    })
  }

I have tried adding both asyncs and awaits to all around the forEach loop, but haven't got it working. I also tried to convert it to for of loop, but got the same (undefined) result.

Thanks a lot in advance!

Edited: resolved this, here's the working version:

  // Get the posts
  async getPosts() {
    for (const id of this.state.topIds) {
      const post = await fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`)

      const thisPost = await post.json()
      this.setState({
        topPosts: [...this.state.topPosts, thisPost]
      })
    }
  }
Antti
  • 13
  • 3
  • `forEach` always returns `undefined` ... so nothing you can do about the forEach returning undefined ... most people use async/await with a regular for loop to do that sort of stuff - did you think that `await posts.json()` would be ALL the responses that you could `.json()` in one go? – Bravo Mar 08 '22 at 10:22
  • 1
    `await json.slice(0, 20)` is pointless. `slice` doesn't return a promise. Welcome to SO, though! You may [find this MDN documentation helpful](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await) in trying to sort out your code. I'd also recommend moving away from the old React class components toward more modern [function components](https://www.robinwieruch.de/react-function-component/). – Andy Mar 08 '22 at 10:30
  • @Bravo that's super-helpful to know, thanks a lot! I now see my mistake. Will edit the original question to include the solution. – Antti Mar 08 '22 at 10:34
  • 1
    don't do that, makes the question useless if the code is correct in the question – Bravo Mar 08 '22 at 10:35
  • 1
    @Andy I've heard the same thing about functional components elsewhere, so definitely will make the change towards them! – Antti Mar 08 '22 at 10:35
  • @Antti, yeah don't recode everything as class/function components will work together, but it's definitely the right move. There's less boilerplate, [lots of useful hooks](https://usehooks.com/) you can use so you don't have to write your own code. State management becomes a lot simpler. – Andy Mar 08 '22 at 10:39
  • The absolutely most important thing to know about `async`/`await` is that **you can only `await` a `Promise`**. This has been hinted at in the other comments. Knowing this and checking this first will help you answer a lot of the questions that have popped up within seconds. `.forEach` doesn't return anything (ie `undefined`), so it can't be awaited. Many other methods will just return the values directly, and can't be awaited. And so on. – Jesper Mar 08 '22 at 10:44
  • Hey @Antti, welcome to StackOverflow. If you were able to resolve the issue on your own, please include it as an answer. It might help someone else facing the same issue. – Shreshth Mar 08 '22 at 10:47

0 Answers0