K3nzie
K3nzie

Reputation: 465

Javascript JSON Fetch with await returns data but its function return an empty array

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?

Upvotes: 1

Views: 1413

Answers (1)

spender
spender

Reputation: 120548

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;
}

Upvotes: 3

Related Questions