JavaScript Promises in For Loops

To use Javascript promises in a for loop, use async/await.

Here is a code skeleton for the correct approach:

async function doSomething() {
    for (item of items) {
        await promiseAction(item)
    }
}

This waits for each promiseAction to complete before continuing to the next iteration in the loop.

For comparison, here is the incorrect approach you were probably thinking of:

function doSomething() {
    for (item of items) {
       promiseAction(item)
    }
}

In this guide, you learn how async/await works and how it solves the problem of using promises in for loops.

The Problem—For Loops Don’t Wait for Async Actions

When you are using promises in a for loop, the loop does not wait for the promises to resolve/reject by default.

This causes unexpected behavior. For example, you may end up trying to access values that are not available yet.

To solve this problem, you need to make the loop wait for the asynchronous actions to complete. This is possible using async/await.

The Solution—Async/Await

Your loop needs to wait for the asynchronous task to complete on each round of the for loop.

To make this happen, mark your function asynchronous using the async keyword, and place an await keyword in front of the action you want to wait for.

Let’s see an example where we first try to run asynchronous code as if it was synchronous. Then, let’s fix the issue by using async/await.

Example—Sending Emails

Say we are sending out a bunch of emails.

Sending an email over the internet is an asynchronous task that takes some unspecified time to complete.

In this example, we build a notification system, that notifies us when an email is sent. Also, when all the emails are successfully sent, the system notifies us once more.

Mail sent to [email protected]
Mail sent to [email protected]
Mail sent to [email protected]
All emails were sent

To simulate this, let’s create:

  • An array of email addresses.
  • A function that simulates sending an email by resolving a promise after one second delay.
  • A function that loops through the array of email addresses, sends each email, and notifies us.

Here is an incorrect approach that does not take into account the asynchronous nature of this task:

const emails = ['[email protected]', '[email protected]', '[email protected]'];

const send = email =>
  new Promise(resolve =>
    setTimeout(() => resolve(email), 1000)
  );

const sendAllEmails = () => {
  for (email of emails) {
    const emailInfo = send(email);
    console.log(`Mail sent to ${emailInfo}`);
  }

  console.log('All emails were sent');
};

sendAllEmails();

Running this piece of code results in an unexpected output:

Mail sent to [object Promise]
All emails were sent

It immediately prints out these notifications to the console without waiting.

So, what went wrong?

The problem is our code runs synchronously, while the tasks are asynchronous. The for loop does not wait for the messages to be sent. Instead, it sends each mail immediately.

So you need to make the for loop wait for each message to be sent.

To do this:

  • Mark the email sending function asynchronous.
  • Await for each send action to complete.

To turn this idea into code:

  • Mark the sendAllEmails() function async. This tells JavaScript that the function is an asynchronous.
  • Wait for the send() function to complete using await keyword.

Here is the updated code:

const emails = ['[email protected]', '[email protected]', '[email protected]'];

const send = email =>
  new Promise(resolve =>
    setTimeout(() => resolve(email), 1000)
  );

const sendAllEmails = async () => {
  for (email of emails) {
    const emailInfo = await send(email);
    console.log(`Mail sent to ${emailInfo}`);
  }

  console.log('All emails were sent');
};

sendAllEmails();

Now, this results in the desired output:

Mail sent to [email protected]
Mail sent to [email protected]
Mail sent to [email protected]
All emails were sent

This completes our guide on promises in for loops.

Conclusion

Today you learned how to use async/await to make Javascript promises work in a for loop.

Never heard about async/await before? I recommend reading Understanding async/await in JavaScript.

To recap, mark the asynchronous function async, and wait for the asynchronous action to complete with await.

Thanks for reading. I hope you find it useful.

Happy coding!

Further Reading

100 JavaScript Interview Questions

50 Buzzwords of Web Development