Search code examples
javascriptes6-promise

How to use promises so that loop doesn't hang


I am trying to loop through records in a database, in order to compile an array (cardsToInsert) that I will write to another database.

I was getting stuck because the array was writing to the database before the loop finished, I know I need to use promises / async functions to achieve what I want, but I'm pretty sure I'm doing something wrong with my promises.

The code works for a few loops (it goes for about 6-10 loops, it's supposed to loop 16 times), but then hangs while trying during wixData.get (or it hangs on a different promise that is part of buildCard).

// wixData.get is a function that returns a promise

async function loopCards(cardsToGet) {
    let writeCard
    let buildCard
    for (let index = 0; index < cardsToGet.length; index++) {

        const cardToGet = cardsToGet[index].card
        buildCard = await wixData.get("Card", cardToGet)
            .then((card) => {
                return card
            })
            .catch((err) => {
                let errorMsg = err;
                return errorMsg
            });
        writeCard = await buildingCard(buildCard)
        cardsToInsert.push(writeCard)
    }
    return cardsToInsert
}

What am I doing wrong? (or what is the key thing I'm doing wrong that is stopping this working, I'm sure there is plenty to be improved here!)

UPDATE

I've now updated the code and it loops through fine.

async function loopCards(cardsToGet) {
console.log('Start')
let writeCard
let buildCard

for (let index = 0; index < cardsToGet.length; index++) {

    const cardToGet = cardsToGet[index].card
    buildCard = wixData.get("Card", cardToGet)
        .then(async (card) => {
            writeCard = await buildingCard(card)
            cardsToInsert.push(writeCard)
        })
        .catch((err) => {
            let errorMsg = err;
            return errorMsg
        });

}
return cardsToInsert
}

How do I get it to wait for the loop to finish before finally returning cardsToInsert?


Solution

  • Your mix of async/await and .then is not really best practice

    This should work, and will return once cardsToInsert is populated

    async function loopCards(cardsToGet) {
        const cardsToInsert = [];
        for (let cardToGet of cardsToGet) {
            try {
                const card = await wixData.get("Card", cardToGet);
                const writeCard = await buildingCard(card);
                cardsToInsert.push(writeCard);
            }
            catch(err) {
                let errorMsg = err;
                return errorMsg;
            }
        }
        return cardsToInsert;
    }
    

    better still, you really don't need to handle any errors here, since the calling function could do that

    So it becomes even simpler

    async function loopCards(cardsToGet) {
        const cardsToInsert = [];
        for (let cardToGet of cardsToGet) {
            const card = await wixData.get("Card", cardToGet);
            const writeCard = await buildingCard(card);
            cardsToInsert.push(writeCard);
        }
        return cardsToInsert;
    }
    

    then using it could be like

    loopCards(cards)
    .then(result => doSomethingWihtResult)
    .catch(error => handleError);
    

    or if calling from an async function

    try {
        let result = await loopCards(cards);
        // do something with result
    } catch(error) {
        // handle Error
    }