1

Take the following scenario:

I need to show in a table a list with all countries and the population of each country. All data can be queried from here: api.population.io.

Thare are 2 api calls that can help me achieve what i want:

  1. http://api.population.io:80/1.0/countries - returns a list of all existing countries
  2. http://api.population.io:80/1.0/population/{$country}/today-and-tomorrow/ - returns the population of a particular country

As you can see i need to make 2 api calls since the second call is dependant of the name of the country made by the first call. I managed to make it work with the initial api call using fetch by using this code:

    fetch('http://api.population.io:80/1.0/countries')
    .then(results => {
        return results.json();
    }).then(data => {
        //data.countries
    })

This just returns me a list with all the countries.

Now i need to loop through data.countries and make a new api call for each country without breaking the whole process. I tried throwing another fetch call where data.countries is available while looping over data.countries but as you can imagine this breaks up the whole process, what i think happens is that the loop doesn't wait for the fetch call to complete thus messing up the query process.

I'm pretty new to this and i've tried googling it but i'm not sure what i can use to achieve what i need. Any help would be truly appreciated. I've been dealing with this problem the whole day

  • Side note: That `fetch` code is missing a check to see if the `fetch` failed; see [my blog post about it](http://blog.niftysnippets.org/2018/06/common-fetch-errors.html). – T.J. Crowder Jun 30 '18 at 18:47
  • See [my answer here](https://stackoverflow.com/a/43766002/157247) to the question [*How do I return the response from an asynchronous call?*](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call), which describes running a bunch of promise-enabled operatinos either in series (one after another) or in parallel (all at once). – T.J. Crowder Jun 30 '18 at 18:49
  • Yes i know about the missing fail check, i'm just playing with it right now but i'll definitely add the missing code. Much appreciated for the tip. – Adrian Savulescu Jun 30 '18 at 19:04

2 Answers2

0

You could fire all the separate population requests at once and use the result when all of them have finished, with the help of Promise.all:

fetch("http://api.population.io:80/1.0/countries")
  .then(results => {
    return results.json();
  })
  .then(data => {
    const populationPromises = data.countries.map(country => {
      return fetch(
        `http://api.population.io:80/1.0/population/${country}/today-and-tomorrow/`
      ).then(results => results.json());
    });

    return Promise.all(populationPromises);
  })
  .then(populations => {
    console.log(populations);
  })
  .catch(error => {
    console.error(error);
  });
Tholle
  • 108,070
  • 19
  • 198
  • 189
  • 1
    I see, that's interesting, i didn't know you can process data like that, i'll definitely try your approach. Thank you so much for the assistance – Adrian Savulescu Jun 30 '18 at 19:04
  • @AdrianSavulescu You're welcome. It takes a while to get a grip of everything you can do with promises. Welcome to stackoverflow! – Tholle Jun 30 '18 at 19:06
0

The approach with async/await makes the code more coherent and readable:

function getCountries() {
    return fetch('http://api.population.io/1.0/countries/?format=json').then(s => s.json())
}

function getPopulation(country) {
    return fetch(encodeURI(`http://api.population.io:80/1.0/population/${country}/today-and-tomorrow/?format=json`)).then(s => s.json())
}

(async () => {

    try {

        const { countries } = await getCountries();
        const populations = await Promise.all(countries.map(getPopulation));

        console.log(populations);

    } catch(err) {
        console.log(err);
    }

})();
Leonid Pyrlia
  • 1,594
  • 2
  • 11
  • 14