How would you fix this issue of nested promises

Hey campers,

I’m using the pokemon API but facing some issues.

so just before the code snippet, a promise was resolved, passing an array of pokemon objects (containing two properties - their name and URL).

for each pokemon in the array, I’d like to make an ajax request using the fetch API to return some data and push that into a new array.

once I’ve received all of that information I’d like to resolve the promise passing the new array of data which I then manipulate further (in the next .then() method)

I’ve managed to achieve all this using a promise nested within a promise, but it’s a messy solution. Does anyone have any ideas on a cleaner way to achieve the same thing, it’d be much appreciated?

thank you.

.then(pokemon_arr => {
		console.log('pokemon are: ', pokemon_arr);
		
		let output = `<div class='card-columns my-3'>`;

		return new Promise(function(resolve, reject) {
			let counter = 0;
			for (let pokemon of pokemon_arr) {
				fetch(`${pokemon.url}`)
				.then(res => res.json())
				.then(pokemon => {

					return new Promise (function (resolve, reject) {
						let dbl_damages_to_arr = [];
						pokemon.types.forEach(type => {
							fetch(`https://cors.io/?http://pokeapi.co/api/v2/type/${type.type.name}`) 
							.then(res => res.json())
							.then(data => {
								
								data = data.damage_relations.double_damage_to;

								if (!dbl_damages_to_arr.includes(data))
									dbl_damages_to_arr.push(data);
									
								if (dbl_damages_to_arr.length == pokemon.types.length) {
									document.getElementById('output').innerHTML += `<p class='text-center'>..almost there</p>`;	
									dbl_damages_to_arr = dbl_damages_to_arr.reduce((a,b) => a.concat(b)); // flatten the array
									resolve({dbl_damages_to_arr, pokemon})
								}
							})
							.catch(err => {
								handleError(err);
								reject(err)
							})
						})
					})	
				})

You can use Promise.all() and Async functions to simplify this code. Something like this (haven’t tested, so you will have to modify it for your needs):

async function find() {
  let pokemon_arr = await getArr()

  let requests = []
  for (let pokemon of pokemon_arr) {
    requests.push( fetch(pokemon.url).then(res => res.json()) )
  }

  let newPokemon = await Promise.all(requests)

  try {
    let typeRequests = []
    for (let type of newPokemon.types) {
      typeRequests.push( fetch(`https://cors.io/?http://pokeapi.co/api/v2/type/${type.type.name}`).then(res => res.json()) )
    }
  
    let data = await Promise.all(typeRequests)
    data = data.damage_relations.double_damage_to
    // etc.
  }
  catch(err) {
    handleError(err)
  }
}

Async functions allow you to instead of nesting promises keep things close to the top level. Promise.all allows you to await an entire series of fetch requests instead of waiting for each to individually finish. I’m not sure that this will totally work, but it should give you enough for something to try.

Let me know how it goes, because this is an interesting scenario.

2 Likes

thank you very much, i havent learnt async methods yet but they look useful

1 Like

They are very useful, but make sure you know promises and promise.all well, or else you will bog your code down like I did at first. Here are some great articles that might help you:

i implemented a simple solution for now, but once i’ m more familiar with async/await i’ll do an update.

new Promise((resolve,reject) => getnPokemon(n, resolve, reject, []))
.then(pokemon_name_arr => {
	let promiseArr = []
	pokemon_name_arr.forEach(pokemon => {
		promiseArr.push(fetch(pokemon.url).then(res => res.json()))
	})
	return Promise.all(promiseArr)
})
.then( //....

again thank you very much.