Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Elegant solution – Nested for loops (eslint no-await-in-loop)

I have some code that works broadly as follows:

function getRegionUsers(region) {
    return new Promise((resolve) => {
        setTimeout(() => {
            if (region === "emea") {
                return resolve(["dave", "mike", "steve"]);
            }
            return resolve(["kiki", "maya", "yukiko"]);
        }, 1000);
    });
}

function sendMail(user, region) {
    console.info(`sendMail called: ${user} (${region})`);
}

async function test() {
    const regions = ["emea", "apac"];
    for (const region of regions) {
        const users = await getRegionUsers(region); // This line is my issue
        for (const user of users) {
            sendMail(user, region);
        }
    }
}
test();

You can ignore the first 2 functions, those just help illustrate the issue. The issue is the 3rd function which causes a linter error (eslint: no-await-in-loop) which makes sense as the await will hold up the execution of each iteration of my for loop.

The solution I keep seeing is to use Promise.all which is fine except I need to keep track of both user and region so I can pass it to the sendMail function. I therefore end up with something like this:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

async function test() {
    const regions = ["emea", "apac"];

    const userPromises = regions.map((r) => getRegionUsers(r));
    const regionUsers = await Promise.all(userPromises);

    for (const [i, region] of regions.entries()) {
        for (const user of regionUsers[i]) {
            sendMail(user, region);
        }
    }
}
test();

This seems messy and inelegant since I essentially duplicate the regions for loop and it’s harder to follow. It also gets messier once you deal with error catching or if you have another level or two of nested for loops.

Does anyone have a more elegant solution (other than just telling ESLint to ignore the line) even if it’s just a neater way to write it?

>Solution :

I would map the regions with an async function. The async function always returns a promise (no need to add an explicit return statement). And the promises can be awaited with Promise.all.

async function test() {
    const regions = ["emea", "apac"];

    const promises = regions.map(async region => {
        const users = await getRegionUsers(region);
        for (const user of users) {
            sendMail(user, region);
        }
    });
    await Promise.all(promises);
}

This should fix the problem reported by no-await-in-loop.
It is possible though that other ESLint rules, depending on your configuration, will start to complain.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading