Search code examples
javascriptarraysreactjspromisefetch

Pushing result from promise.all into an array


After all my promises have been resolved, I would like to return an Array with all the response data.

For some reason when I return Pokémon outside the promise, all I get is an empty array. However, inside the promise, after the 5th time, I get the array I want with 5 objects.

The interesting part is in my main function Pokegame, I also console.log teamOne, and I get an empty array ("[]"), but the same properties as the console.log from inside, however I cant access the keys in this one. I've tried different things but for some reason I just can't return a "normal" array from the function getPokemons.

import React from "react";
import Pokedex from "./Pokedex";
import Pokecard from "./Pokecard";

var PokeAPI = "https://pokeapi.co/api/v2/pokemon/";

const getPokemonIDs = () => {
  var team = [];
  while (team.length < 5) {
    var randomPokemonID = Math.ceil(Math.random() * 807);
    team.push(randomPokemonID);
  }
  return team;
};

const getPokemons = () => {
  var pokemonIDs = getPokemonIDs();
  var pokemons = [];
  Promise.all(pokemonIDs).then(pokemonIDs => {
    for (let x in pokemonIDs) {
      fetch(PokeAPI + pokemonIDs[x])
        .then(response => response.json())
        .then(response => {
          pokemons.push(response);
          console.log("===inside===");
          console.log(pokemons);
        })
        .catch(error => {
          console.log(error);
        });
    }
  });
  console.log("==end==");
  console.log(pokemons.length);
  return pokemons;
};

const Pokegame = () => {
  const teamOne = getPokemons();
  console.log("==teamOne===")
  console.log(teamOne);
  
  return (
    <div>
      <Pokedex team={teamOne} />
    </div>
  );
};

export default Pokegame;

Console Output

    ===inside===
    index.js:27 (5) [{…}, {…}, {…}, {…}, {…}]
    index.js:27 ==end==
    index.js:27 0

    ===teamOne===
    index.js:27 []
    0: {abilities: Array(2), base_experience: 63, forms: Array(1), game_indices: Array(0), height: 3, …}
    1: {abilities: Array(1), base_experience: 72, forms: Array(1), game_indices: Array(20), height: 6, …}
    2: {abilities: Array(3), base_experience: 175, forms: Array(1), game_indices: Array(9), height: 13, …}
    3: {abilities: Array(3), base_experience: 81, forms: Array(1), game_indices: Array(17), height: 5, …}
    4: {abilities: Array(2), base_experience: 238, forms: Array(1), game_indices: Array(4), height: 16, …}
    length: 5
    __proto__: Array(0)
 
    index.js:27 ===inside===
    index.js:27 [{…}]

    index.js:27 ===inside===
    index.js:27 (2) [{…}, {…}]

    index.js:27 ===inside===
    index.js:27 (3) [{…}, {…}, {…}]

    index.js:27 ===inside===
    index.js:27 (4) [{…}, {…}, {…}, {…}]

    index.js:27 ===inside===
    index.js:27 (5) [{…}, {…}, {…}, {…}, {…}]
    0: {abilities: Array(2), base_experience: 63, forms: Array(1), game_indices: Array(0), height: 3, …}
    1: {abilities: Array(1), base_experience: 72, forms: Array(1), game_indices: Array(20), height: 6, …}
    2: {abilities: Array(3), base_experience: 175, forms: Array(1), game_indices: Array(9), height: 13, …}
    3: {abilities: Array(3), base_experience: 81, forms: Array(1), game_indices: Array(17), height: 5, …}
    4: {abilities: Array(2), base_experience: 238, forms: Array(1), game_indices: Array(4), height: 16, …}
    length: 5
    __proto__: Array(0)

I know that the promise will get put on the callback queue stack and that's why it returns an empty array the first time, however I do not understand why console.log(teamOne) or even console.log(pokemons) would log an empty array with similar attributes?

https://codesandbox.io/s/competent-resonance-iq1h2


Solution

  • The issue with Promise.all() is that there are actually not any promises. Promise.all() accepts an array of promises, while you are passing an array of IDs. Try this (logging removed):

    Promise.all(
      pokemonIDs.map(id => {
        return fetch(PokeAPI + id)
          .then(response => response.json())
          .then(result => {
            pokemons.push(result);
          });
      })
    ).then(() => {
      console.log("==end==");
      console.log(pokemons.length);
      console.log(pokemons);
    });
    

    Forked sandbox here: https://codesandbox.io/s/serverless-breeze-bozdm?file=/src/components/Pokegame.js (slightly edited to integrate with React).

    Personally, I like to use Promise.all() to run parallel requests, but for other cases, I prefer async / await as it is very easy to write and read without too much extra thinking. The original example can then be rewritten like this (provided the parent function is async)

    await Promise.all(
      pokemonIDs.map(id => {
        return fetch(PokeAPI + id)
          .then(response => response.json())
          .then(result => {
            pokemons.push(result);
          });
      })
    );
    
    console.log("==end==");
    console.log(pokemons.length);
    console.log(pokemons);