Why setTimeout Doesn't Work in Loops: Results Coming Together After Timeout

You're trying to make "Hello" appear after a 5-second delay, and you want it to happen 10 times. You created a function called printHello with a 5-second delay using setTimeout, and then you ran this function 10 times in a loop.

Here's the code:

function printHello() {
  setTimeout(function () {
    console.log("Hello");
  }, 5000);
}

for (let i = 0; i < 10; i++) {
  printHello();
}

However, you notice that all the "Hello" messages are printed together after 5 seconds.

The question is, why doesn't setTimeout work as expected in a loop?

Loops operate synchronously, meaning they go through all their iterations effectively "at once." In this case, each printHello() in the loop is getting called simultaneously, one after the other. Consequently, each function starts its 5-second timer at the same time, leading to all the timers ending simultaneously.

There are several ways to address this.

1. Use setInterval

One way is to use setInterval(). When you call it once, it keeps running a function repeatedly every few milliseconds.

You'd need to keep track of how many times it runs, and then call clearInterval() when it's over to stop the looping.

let counter = 0;
const intervalId = setInterval(function () {
  console.log("Hello");
  counter++;

  if (counter === 10) {
    clearInterval(intervalId); // Stop the interval after 10 iterations
  }
}, 5000);

This way, the callback inside setInterval will be executed every 5 seconds, and you can control the number of iterations by incrementing the counter variable and clearing the interval when the desired count is reached.

2. Use the Loop Variable to Manage Delays

Utilize the loop variable to determine the delay for each function call. Adjust the delay by multiplying the loop variable with the desired time interval. In this case, the code could look like this:

function printHello() {
  console.log("Hello");
}

for (let i = 1; i <= 10; i++) {
  setTimeout(function () {
    printHello();
  }, i * 5000);
}

3. Controlled Delays Using async and await

Transform your functions into async functions and leverage the await keyword along with the provided sleep function for controlled delays:

let sleep = (dur) => new Promise((res) => setTimeout(res, dur));

async function runLoop() {
  for (let i = 0; i < 10; i++) {
    console.log("Hello");
    await sleep(500); // Wait for 500 milliseconds
  }
}

// Call the async function to start the loop
runLoop();

I have created a set of resources for learning asynchronous JavaScript. These guides—Callbacks, Promises, and Async/Await —cover everything I’ve learned from years of real-world JavaScript experience.

If you found this article helpful, you’ll get so much out of these guides. Each one is optimized for those “lightbulb moments,” building a strong mental model for how asynchronous JavaScript works and how you can use it to create fast, dynamic applications.