setTimeout Inside Loop Issue in JavaScript

Introduction

JavaScript is single-threaded but handles asynchronous operations using features like callbacks, event loop, and timers. One common source of confusion for developers is using setTimeout() inside a loop—especially when the output is not what they expect.

Many beginners assume that setTimeout() executes sequentially within a loop, but due to JavaScript’s asynchronous nature and variable scope, the behavior can be surprising.

In this article, we will clearly understand why this issue happens, how it works internally, and how to fix it using modern JavaScript techniques.

What is setTimeout Inside Loop Issue?

The setTimeout inside loop issue occurs when you use setTimeout() within a loop, and all callbacks print the same value instead of the expected sequential values.

This usually happens because:

  1. setTimeout() executes asynchronously
  2. Variables declared with var are function-scoped, not block-scoped
  3. The loop completes before setTimeout() callbacks execute

Basic Syntax of setTimeout()


setTimeout(function, delay);

Example:


setTimeout(() => {
 console.log("Hello");
}, 1000);

Show Problem in Example

Let’s see the issue:


for (var i = 1; i <= 5; i++) {
 setTimeout(function () {
   console.log(i);
 }, 1000);
}

Expected Output:

1
2
3
4
5

Actual Output:

6
6
6
6
6

Explanation of this issue happened

I will explain step by step.

  1. Loop runs quickly from 1 to 5
  2. setTimeout() schedules callbacks
  3. Loop finishes → i becomes 6
  4. After 1 second, callbacks execute
  5. All callbacks reference same i → 6

Note: Because var is function-scoped, not block-scoped.

Solutions of this issue

Solution 1: Use let (Best & Modern Way)


for (let i = 1; i <= 5; i++) {
 setTimeout(function () {
   console.log(i);
 }, 1000);
}

Output:

1
2
3
4
5

Note: let is block-scoped, so each iteration gets its own value.

 Solution 2: Using IIFE (Closure)

Creates a new scope for each iteration.


for (var i = 1; i <= 5; i++) {
 (function (x) {
   setTimeout(function () {
     console.log(x);
   }, 1000);
 })(i);
}

Solution 3: Using setTimeout with Parameter


for (var i = 1; i <= 5; i++) {
 setTimeout(function (i) {
   console.log(i);
 }, 1000, i);
}

Real-Life Example of Showing Notifications One by One

❌ Wrong Way:


for (var i = 1; i <= 3; i++) {
 setTimeout(() => {
   console.log("Notification " + i);
 }, i * 1000);
}

Output:

Notification 4
Notification 4
Notification 4

Correct Way:


for (let i = 1; i <= 3; i++) {
 setTimeout(() => {
   console.log("Notification " + i);
 }, i * 1000);
}

Output:

Notification 1
Notification 2
Notification 3

Common Mistakes when using setTimeout Inside a Loop

There are some common mistakes when using setTimeout inside a Loop.

1. Using var instead of let


for (var i = 0; i < 5; i++) { }

2. Assuming setTimeout is synchronous


console.log("Start");
setTimeout(() => console.log("Middle"), 1000);
console.log("End");

Output:

Start
End
Middle

3. Ignoring Event Loop behavior

JavaScript executes

  1. Call Stack
  2. Web APIs
  3. Callback Queue
  4. Event Loop

4. Wrong delay assumptions


for (let i = 1; i <= 5; i++) {
 setTimeout(() => console.log(i), 1000);
}

Note: In this example, all run after 1 second, not sequentially.

Interview Questions

Q 1: What is the setTimeout inside loop issue?
Ans: It occurs when all callbacks print the same value due to asynchronous execution and variable scoping.
Q 2: Why does var cause problems?
Ans: Because var is function-scoped and shares the same variable across iterations
Q 3: How does let fix the issue?
Ans: let is block-scoped and creates a new variable for each loop iteration.
Q 4: Can we solve it without let?
Ans: Yes, using:
IIFE
setTimeout parameters
Q 5: What is the event loop?
Ans: A mechanism that handles asynchronous execution in JavaScript

Conclusion

In this tutorial, you learned how JavaScript handles asynchronous code and variable scoping. While it may seem confusing at first, once you understand concepts like event loop, closures, and block scope, it becomes much easier to handle.

Fix Asynchronous JavaScript Issues