0

I have issues with handling error 404. What I want to do is, the data I get from making a request to the PokeApi, I want to push it into an array. If all request are successful to the API and pushed into the array, everything works fine, then I can get out my data I want, move it to my then() and append it to a div. However, once a request are getting a 404 error not found, nothing gets appended to my div. How do I handle the 404 and continue to loop and be able to append my data?

I have tried, using if statements and fail(), done() but just doesn't work as I want it to.

function get_sprites(poke_name){
 var arr = [];
 for(var i = 0; i<poke_name.length; i++){
     arr.push($.getJSON("https://pokeapi.co/api/v2/pokemon/" + 
     poke_name[i]));
 }
 $.when.apply($, arr).then(function(){
    var storeObjPoke = [];
    for(var i = 0; i < arguments.length; i++){
        var name = arguments[i][0].name
        var upperCase_name = name.charAt(0).toUpperCase() + 
        name.slice(1);
        var objPoke = {
        id : arguments[i][0].id,
        name : upperCase_name,
        imgUrl : arguments[i][0].sprites.front_default
      }
      $("<div class='pokemon' id='"+ arguments[i][0].id +"'>" + 
        upperCase_name + "<br><img src='" +arguments[i] 
        [0].sprites.front_default+"'alt=''/><br>" + arguments[i][0].id + 
       "</div>" ).appendTo("#display_pokemon");
    }
 }
}

I expect it to be able to display all my objects in my div, but when I get a 404 error, nothing gets appended to div.

isherwood
  • 58,414
  • 16
  • 114
  • 157
talheiman
  • 13
  • 1

1 Answers1

-1

So it looks like .when is similar to promise.all with the fact that it's an all or nothing kinda thing. All promises have to be resolved (successful) for the .then function to be fired.

So what is happening for you is once the promise with the 404 is rejected, jQuery calls the .fail() function immediately (which you don't have so its swallowing the error) and the rest of the promises may be unfulfilled and .then() is not called.

From jQuery.when()

In the case where multiple Deferred objects are passed to jQuery.when(), the method returns the Promise from a new "master" Deferred object that tracks the aggregate state of all the Deferreds it has been passed. The method will resolve its master Deferred as soon as all the Deferreds resolve, or reject the master Deferred as soon as one of the Deferreds is rejected.

and

In the multiple-Deferreds case where one of the Deferreds is rejected, jQuery.when() immediately fires the failCallbacks for its master Deferred. Note that some of the Deferreds may still be unresolved at that point.

so what you are trying to do doesn't seem possible without some extra fluff. You can check out this SO answer to get a more general understanding of some of the fluff you might need Wait until all ES6 promises complete, even rejected promises including a future proposal promise. allSettled() which would solve your problem, but its only compatible with a few browsers currently.

2 other thoughts

  1. Is it important that you wait for all requests to come back before you start displaying them in the UI? If not then just call the requests in a loop.

For example,

get_sprites(["squirtle", "charmander", "not_a_pokemon", "bulbasaur"])

function get_sprites(poke_name) {
  for (let i = 0; i < poke_name.length; i++) {
    $.getJSON("https://pokeapi.co/api/v2/pokemon/" + poke_name[i], addPokemon)
      .fail(function(err) {
        console.log(`error getting pokemon: ${poke_name[i]} - ${err.status}`);
      });
  }

  function addPokemon(pokemon) {
    var storeObjPoke = [];
    var name = pokemon.name
    var upperCase_name = name.charAt(0).toUpperCase() + name.slice(1);
    var objPoke = {
      id: pokemon.id,
      name: upperCase_name,
      imgUrl: pokemon.sprites.front_default
    }
    $("<div class='pokemon' id='" + pokemon.id + "'>" +
      upperCase_name + "<br><img src='" + pokemon.sprites.front_default + "'alt=''/><br>" + pokemon.id +
      "</div>").appendTo("#display_pokemon");
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="display_pokemon"></div>
  1. If it is important to not show anything until all requests are finished, just keep a counter on the requests finished using .always() on the request and check after each request if the counter equals poke_name.length. If so, then you know a request for each pokemon has finished (success or fail) and then display all the results.

example of 2

get_sprites(["squirtle", "charmander", "not_a_pokemon", "bulbasaur", "onix", "mewtwo", "pikachu"])

function get_sprites(poke_names) {
  let poke_names_length = poke_names.length;
  let finished_requests = 0;
  let poke_array = [];

  for (let poke_name of poke_names) {
    $.getJSON("https://pokeapi.co/api/v2/pokemon/" + poke_name)
      .always(function() {
        finished_requests++
      })
      .done(addPokemon)
      .fail(function(err) {
        console.log(`error getting pokemon: ${poke_name} - ${err.status}`);
      })
  }

  function addPokemon(pokemon) {
    let upperCase_name = pokemon.name.charAt(0).toUpperCase() + pokemon.name.slice(1);

    let objPoke = {
      id: pokemon.id,
      name: upperCase_name,
      imgUrl: pokemon.sprites.front_default
    }

    poke_array.push(objPoke);

    if (poke_names_length === finished_requests) {
      displayPokemon();
    }
  }

  function displayPokemon() {
    let ordered_pokes = poke_array.sort(function(a, b) {
      return a.id - b.id;
    });

    for (let poke of ordered_pokes) {
      $("<div class='pokemon' id='" + poke.id + "'>" +
        poke.name + "<br><img src='" + poke.imgUrl + "'alt=''/><br>" + poke.id + "</div>").appendTo("#display_pokemon");
    }
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="display_pokemon"></div>
Andrew Lohr
  • 5,380
  • 1
  • 26
  • 38
  • Hi Andrew, thanks for answering my first post ever on stack. The reason why i want to wait until all requests are finished, is because i want to display my pokeobjects in the correct order by wanting to push it into a array and sort it by id. – talheiman Sep 05 '19 at 16:45
  • @talheiman I updated my answer with an example of how you can wait for all before displaying them (so you can sort/order them by id). It's essentially just keeping a counter of each finished request (success or fail) and when that matches the `poke_names` array we know there's a request for each pokemon has finished and we can sort and order the ones that succeeded. – Andrew Lohr Sep 05 '19 at 17:13