Search code examples
javascriptnode.jses6-promise

Nested Promises - Map function inside a map function


basically I have a big array with object values like this:

[{
  - Champion Name
  - [Another Array with Skins information]
}]

so what I want to do is run a map function inside the second array to get a value for everyone of these skins so what I did was.

const got = require('got');
const cheerio = require('cheerio');
const fs = require('fs').promises;

const getSkinData = async champSkins => {
  const data = JSON.parse(champSkins);

  const handleMap = skin => {
    return new Promise(async (resolve, reject) => {
      try {
        const name = skin.split(' ').join('_');

        const { body } = await got(`https://lol.gamepedia.com/${name}`);

        const $ = cheerio.load(body);

        const skinLink = $('.InfoboxSkin img').attr('src') || '';

        const skinInfo = {
          skinName: skin,
          skinLink
        };

        resolve(skinInfo);
      } catch (err) {
        console.error(err.message);
      }
    });
  };

  Promise.all(
    data.map(async ({ skins }) => {
      return Promise.all(
        skins.map(skin => {
          return handleMap(skin);
        })
      );
    })
  ).then(data => console.log(data));
};

module.exports = getSkinData;

but it didn't actually work, I couldn't access the data and the console.log inside the promise at the end didn't even run.

any idea how to do it or is there a better way to do it?

EDIT #1

basically 'skins' for every champ is an array of strings like:

[ 'Aatrox',          
  'Justicar Aatrox', 
  'Mecha Aatrox',    
  'Sea Hunter Aatrox'
  'Blood Moon Aatrox'
  'Blood Moon Aatrox 
  'Victorious Aatrox' ]

[ 'Ahri',            
  'Dynasty Ahri',    
  'Midnight Ahri',   
  'Foxfire Ahri',    
  'Popstar Ahri',    
  'Challenger Ahri', 
  'Academy Ahri',    
  'Arcade Ahri',     
  'Star Guardian Ahri
  'K/DA Ahri',       
  'K/DA Ahri Prestige
  'Elderwood Ahri' ] 

and skin is just every value of the array

and the value that comes back from the scraping is just a link for each skin

enter image description here


Solution

  • const getSkinData = async champSkins => {
        const data = JSON.parse(champSkins);
        // async that returns promise is a double promise, you only need async
        const handleMap = async skin => {
            try {
                const name = skin.split(" ").join("_");
                const { body } = await got(`https://lol.gamepedia.com/${name}`);
                const $ = cheerio.load(body);
                const skinLink = $(".InfoboxSkin img").attr("src") || "";
                const skinInfo = {
                    skinName: skin,
                    skinLink
                };
                // changed resolve to return
                return skinInfo;
            } catch (err) {
                console.error(err.message);
            }
        };
    
        return await Promise.all(data.map(({ skins }) => Promise.all(skins.map(skin => handleMap(skin)))));
    };
    

    Async functions return a promise that resolves with their return value, so returning a promise from an async function is redundant and ends up causing a nested promise. Also, you don't have to use .then in async functions because you can just await the promise. Read more about Promises and async/await.