1

I'm trying to fiddle with fetching data from public APIs and then showing that data in a React component, the react part is not important tho, this is primarly a js issue. I'm using PokeApi for learning purpose, I create a new object with the data I need from the request, and then push that object to an array that the function returns when called:

    // fetchPoke() function 
    let pokemons = []
    // IDs is just an array with 20 random numbers between a range
    IDs.forEach(async (id) => {
        let url = `https://pokeapi.co/api/v2/pokemon/${id}`
        
        await fetch(url)
        .then((res) => {
            if (!res.ok) {
            console.log(`${id} not found`)
            } else { 
                
                return res.json()
            }

        })
        .then((data) => {
            
            let pokemon = {}
            pokemon.id = data.id
            pokemon.name = data.name
            pokemon.image = data.sprites.other.dream_world.front_default
            pokemons.push(pokemon)
            

        })
    })
    // function returns the array of object 
    return pokemons

But whenever I call this function

let pokemons = fetchPoke()

And then log it

console.log(pokemons)

Although I can see the content, it says it's an array of 0:

the log of pokemons variable

In fact if I try to console log pokemons.length I get 0

What could cause this? Am I doing something wrong in my fetch request?

K3nzie
  • 445
  • 1
  • 7
  • 20
  • 1
    You may want to view this answer: https://stackoverflow.com/questions/72799361/are-async-await-async-or-synchronous for clarity on how `async/await` work. It's important to remember that even though it looks synchronous it's not. The function sleeps while the promise is waiting to settle. Therefore, your code will make 20 async calls and then continue on to log `pokemons` which is an empty array at that point. – War10ck Nov 15 '22 at 21:17

1 Answers1

3

So, you create an empty array.

You loop through you loop through the array, firing-off a bunch of asynchronous requests that will, as a side-effect, populate the empty array when the promises complete.

You immediately return the array without waiting for the promises to complete.

The requests probably have not even left your machine by the time this happens.

The array is empty at this point.

If instead, you declare your function as async and you map your IDs to a new array of promises, then await them all with Promise.all, then the promises will be able to complete before the function returns, and the resolved value of Promise.all will be an array containing your pokemons.

async function getSomePokemons(IDs) { // note, this is an async function
    const promises = IDs.map((id) =>
        fetch(`https://pokeapi.co/api/v2/pokemon/${id}`)
            .then((res) => {
                if (!res.ok) {
                    console.log(`${id} not found`)
                    // implicitly resolves as undefined
                } else {
                    return res.json()
                }
            })
            .then((data) => (data ? { // data might be undefined
                id: data.id,
                name: data.name,
                image: data.sprites.other.dream_world.front_default
            } : undefined))
    )
    const pokemons = await Promise.all(promises);
    return pokemons;
}
spender
  • 117,338
  • 33
  • 229
  • 351
  • I genuinely thought that by the time you get to `.then(data)` the single promise is resolved and you could pass the data to your variable, if I console logged the promise it would say "fullfilled", so I guess that threw me off. – K3nzie Nov 16 '22 at 00:26