1

I am using pokeapi and I am creating a object inside a promise for every pokemon but I have to get the information that is in .species and is in another URL so I am calling another fetch but doesn´t give me the data.

const fetchPokemon = () => {
  const promises = [];
  for (let index = 1; index <= 150; index++) {
    const url = `https://pokeapi.co/api/v2/pokemon/${index}`;
    promises.push(fetch(url).then((res) => res.json()));
  }
  Promise.all(promises).then((results) => {
    const pokemon = results.map((data) => ({
      name: data.name,
        egg_groups:  getEgg(data)
    }));

And the method that I am calling in egg_groups is like this:

return fetch(data.species.url).then((response) => response.json()).then(
    (res)=> res.egg_groups.map((egg_group) => egg_group.name).join(" and "));

The result of egg_groups()

How I can get the "monster and plant" rather than the entire promise?

Guzzs
  • 21
  • 1
  • 4
  • Uh, just wait for it using `.then()`, as you already did successfully lots of times in your code? – Bergi May 23 '22 at 15:36

4 Answers4

2

You can call the getEgg function for every result in the results array and then chain it with .then and resolve the resulting array using Promise.all.

const fetchPokemons = () => {
  const promises = [];
  for (let index = 1; index <= 2; index++) {
    const url = `https://pokeapi.co/api/v2/pokemon/${index}`;
    promises.push(fetch(url).then((res) => res.json()));
  }
  return Promise.all(promises).then((results) =>
    Promise.all(results.map((data) =>
      getEgg(data).then((egg) => ({
        name: data.name,
        egg_groups: egg,
      }))
    ))
  );
};

function getEgg(data) {
  return fetch(data.species.url)
    .then((response) => response.json())
    .then((res) =>
      res.egg_groups.map((egg_group) => egg_group.name).join(" and ")
    );
}

fetchPokemons().then((pokemons) => pokemons.map(poke => {
  document.body.innerHTML += `<p>${poke.name} - ${poke.egg_groups}</p>`
}));

You can also generate the data for each pokemon inside the getEgg function (renamed to getData), as shown below:

const fetchPokemons = () => {
  const promises = [];
  for (let index = 1; index <= 2; index++) {
    const url = `https://pokeapi.co/api/v2/pokemon/${index}`;
    promises.push(fetch(url).then((res) => res.json()));
  }
  return Promise.all(promises).then((results) =>
    Promise.all(results.map(getData))
  );
};

function getData(data) {
  return fetch(data.species.url)
    .then((response) => response.json())
    .then((res) => ({
      name: data.name,
      egg_groups: res.egg_groups
        .map((egg_group) => egg_group.name)
        .join(" and "),
    }));
}

fetchPokemons().then((pokemons) =>
  pokemons.map((poke) => {
    document.body.innerHTML += `<p>${poke.name} - ${poke.egg_groups}</p>`;
  })
);
SSM
  • 2,855
  • 1
  • 3
  • 10
  • I'd recommend to keep the object creation in `fetchPokemon` though – Bergi May 23 '22 at 15:39
  • How can I convert that console.log to an array to use it in others functions? – Guzzs May 23 '22 at 15:54
  • 1
    @GustavoMéndez your other functions would all need to be within the .then callback, or interact with the promise through their own .then calls. – Kevin B May 23 '22 at 15:54
  • @GustavoMéndez I've updated my answer have a look, the response is an array, so you can loop over & use the items as per your need. – SSM May 23 '22 at 15:59
  • 2
    @Bergi I've added that approach as well, I hope that is what you were referring to. – SSM May 23 '22 at 16:14
2

I would suggest using the async and await syntax, this allows you to structure asynchronous code in a more readable way.

We'd create a getPokemon() function that accepts an index argument. Once we retrieve the pokemon object using fetch() we can then get the eggs using the same function.

We can then return an object including the pokemon name, the egg_groups etc.

We can then also create a getPokemonArray() function that will fetch an array of pokemons, taking a start and end index as arguments:

async function getPokemon(index) {
    const url = `https://pokeapi.co/api/v2/pokemon/${index}`;
    const pokemon = await fetch(url).then((res) => res.json());
    const eggs = await fetch(pokemon.species.url).then((response) => response.json());
    const egg_groups = eggs.egg_groups.map((egg_group) => egg_group.name).join(" and ");
    return { index, name: pokemon.name, egg_groups };
}

async function getPokemonArray(startIndex, endIndex) {
    let promises = [];
    for (let index = startIndex; index <= endIndex; index++) {
        promises.push(getPokemon(index));
    }
    let pokemonArray = await Promise.all(promises);
    console.log('Pokemon array:', pokemonArray);
    return pokemonArray;
}

getPokemonArray(1,3);

getPokemonArray(40,42);
.as-console-wrapper { max-height: 100% !important; }
Terry Lennox
  • 29,471
  • 5
  • 28
  • 40
  • 1
    This example really helped me understand how promises work. But how can I get the data as the return value of the function? Does working with fetched data always have to be done inside the getPokemonArray function? Let's say I want to sort the data. I have to call the sort function in the getPokemonArray function? – Milan Panin Aug 01 '22 at 00:21
  • 1
    You could sort the data in the getPokemonArray function, or you could call getPokemonArray from another function as long as it's asynchronous and sort the data there. You'd have to use let arr = await getPokemonArray() or something like that. – Terry Lennox Aug 01 '22 at 07:38
  • Thank you so much, top level await solve my problem! – Milan Panin Aug 01 '22 at 10:42
0

You should insert the async keyword for the map function to be async to use await there. Since you need to "await" for the promise to resolve to get the result.

Promise.all(promises).then((results) => {
  const pokemon = results.map(async (data) => ({
    name: data.name,
    egg_groups: await getEgg(data)
  }));
  return pokemon;
});
Dejan
  • 78
  • 5
  • No, [you should not wrap it into another `new Promise`](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi May 23 '22 at 15:37
  • 1
    @bergi Thanks for bringing this to my attention, I edited the answer now. – Dejan May 23 '22 at 15:46
0

you can also add another await promise when you are mapping the results of your first query, since the data is also containing a function that returns a promise you should be waiting for that response too.

const fetchPokemon = () => {
  const promises = [];
  for (let index = 1; index <= 150; index++) {
    const url = `https://pokeapi.co/api/v2/pokemon/${index}`;
    promises.push(fetch(url).then((res) => res.json()));
  }
  Promise.all(promises).then(async (results) => {
    const pokemonPromise = results.map((data) => ({
      name: data.name,
        egg_groups:  getEgg(data)
    }));
    await Promise.all(pokemonPromise)
  }
}
Manuel Duarte
  • 644
  • 4
  • 18