When you want to delay a task or run something again and again after a time gap, JavaScript has two timer functions just for that:
setTimeout
: Runs a function once after a delay.setInterval
: Runs a function again and again with a fixed time gap.
Let’s understand each one of them.
setTimeout
setTimeout
runs a function once after a delay.
JavaScript is non-blocking and asynchronous. That means, it doesn’t block your code and continues running the rest and comes back later after the delay to run the code inside the setTimeout
.
Syntax:
setTimeout(callback, delayInMs, ...args);
Here,
callback
: Function to rundelayInMs
: Time in milliseconds (1000ms = 1s)...args
: Optional arguments passed to the callback
Example:
console.log("Before!");
setTimeout(() => {
console.log("Runs after 2 seconds!");
}, 2000);
console.log("After!");
// Output:
// Before!
// After!
// Runs after 2 seconds!
Here,
console.log("Before!")
runs immediately.setTimeout(() => { ... }, 2000)
:- JavaScript sets a timer for 2000 milliseconds (2 seconds).
- It schedules the function inside to run later, after 2 seconds.
- But it doesn't wait and continues running the rest of the code.
console.log("After!")
runs right after the timeout is scheduled.- After 2 seconds, the function inside
setTimeout
finally runs andRuns after 2 seconds!
gets printed to the console. - So the final output is:
- Before!
- After!
- Runs after 2 seconds!
Let's take another example.
function greet(name) {
console.log("Hello", name);
}
setTimeout(greet, 1000, "Shefali"); // Output: Hello Shefali (after 1 sec)
Here,
function greet(name) {...}
is a function and it takes aname
and logs"Hello", name
to the console.setTimeout(greet, 1000, "Shefali");
greet
: function to run1000
: delay in milliseconds (1 second)"Shefali"
: argument passed togreet
- After 1 second, JavaScript automatically calls:
greet("Shefali");
- So the output is
Hello Shefali
(after 1 second).
setTimeout with 0 delay
Even if you set the delay to 0, the callback still goes to the event queue and runs after the current code finishes.
For example:
console.log("First!");
setTimeout(() => {
console.log("Third!");
}, 0);
console.log("Second!");
// Output:
// First!
// Second!
// Third!
Here,
console.log("First!")
runs immediately, it's synchronous.setTimeout(..., 0)
schedules the callback to run after the current call stack is empty, not instantly.console.log("Second!")
runs right after, also synchronous.- Once the main code finishes, the event loop checks the task queue.
- The
setTimeout
callback is waiting there, and now it's executed, printing"Third!"
. - Even with
0ms
delay, the function is deferred and will always run after all synchronous code finishes.
clearTimeout
You can use clearTimeout
to clear the timeout scheduled with setTimeout
.
const id = setTimeout(() => {
console.log("This will never run!");
}, 3000);
clearTimeout(id); // Cancels the timeout
Here,
- You set a timeout to run a function after 3 seconds.
- The function is scheduled to print "This will never run!" after 3000 milliseconds (3 seconds).
setTimeout
returns anid
that represents this scheduled task.- Immediately after, you call
clearTimeout(id);
, this cancels the scheduled timeout using theid
. - So the callback function never executes.
Why Use This?
To stop a timeout before it runs, for example:
- If a user navigates away from a page.
- If a condition changes and you no longer want the delayed action.
- To prevent unnecessary work or errors.
setInterval
setInterval
runs a function again and again with a fixed time gap.
This keeps going forever unless you stop it. That means your function will run again and again, on a fixed schedule, until you clear it using clearInterval()
.
Syntax:
setInterval(callback, intervalInMs, ...args);
Here, it runs the callback
every intervalInMs
milliseconds until you stop it.
Example:
setInterval(() => {
console.log("Runs every 1 second!");
}, 1000);
Here,
setInterval
schedules a function to run repeatedly every 1000 milliseconds (1 second).- The function inside logs: Runs every 1 second!
clearInterval
You can use clearInterval
to stop the interval.
const id = setInterval(() => {
console.log("This will be stopped!");
}, 1000);
setTimeout(() => {
clearInterval(id);
console.log("Stopped!");
}, 5000);
Here,
const id = setInterval(() => {...}, 1000);
runs the function every 1 second.- Every second, it logs: "This will be stopped!".
- The interval’s ID is saved in
id
, which you’ll use to stop it later. - Then you set a timeout to stop the interval:
setTimeout(() => {...}, 5000);
. - After 5 seconds, the
setTimeout
callback runs. - It calls
clearInterval(id)
to stop the repeating interval. - Then logs "Stopped!" once.
- When you run this, for the first 5 seconds, every second you see:
This will be stopped!
. - After 5 seconds:
Stopped!
- And the interval stops and there will be no more logs from it.
Why use this?
- Sometimes you want to run repeated actions but only for a limited time.
- Use
setTimeout + clearInterval
combo to schedule when to stop the repeating action.
What is a Timer ID in setTimeout and setInterval?
Whenever you call setTimeout()
or setInterval()
, JavaScript returns a unique timer ID, just a number that represents that scheduled task.
You can store this ID in a variable, so you can cancel or clear it later.
Example:
const id = setTimeout(() => {
console.log("Hello after 2 seconds!");
}, 2000);
Here, id
will store a number (like 1
, 2
, etc.) that represents this timer.
If you change your mind and don’t want this to run:
clearTimeout(id);
Same goes for intervals:
const intervalId = setInterval(() => {
console.log("Repeats every second!");
}, 1000);
// Later, stop it
clearInterval(intervalId);
Why store the ID?
Because setTimeout
and setInterval
just schedule the task. You need their IDs to cancel them before they execute or repeat again.
The this Context in Timer Callbacks
When using timers with regular functions (not arrow functions), be careful about the this
context.
const obj = {
name: "Shefali",
greet() {
console.log("Hello", this.name);
},
// This won't work as expected
delayedGreet() {
setTimeout(function () {
console.log("Hello", this.name); // `this` is undefined or window
}, 1000);
},
// This works correctly
delayedGreetFixed() {
setTimeout(() => {
console.log("Hello", this.name); // Arrow function preserves `this`
}, 1000);
},
};
obj.greet(); // Output: Hello Shefali
obj.delayedGreet(); // Output: Hello <empty string>
obj.delayedGreetFixed(); // Output: Hello Shefali
Why this happens:
- Regular functions in timers lose their original
this
context. - Arrow functions inherit
this
from their surrounding scope. - Use arrow functions or
.bind()
to preserve the correctthis
.
Common Mistakes
-
Forget to clear interval: It keeps running and wastes resources.
-
Wrong delay:
setTimeout(fn, "1000")
works, but always use numbers, not strings. -
Assuming it's exact: Timers aren’t guaranteed to be precise. JavaScript is single-threaded and delay may stretch slightly if the main thread is busy. For example:
for (let i = 0; i < 1000000000; i++) { // Blocking operation } setTimeout(() => console.log("This might be delayed"), 100);
Best Practices
- Use
setTimeout
for delayed tasks (like showing a popup). - Use
setInterval
for polling, updating time, animations, but always clear it when done. - Store the timer ID in a variable if you plan to cancel it.
- For smoother animations, prefer
requestAnimationFrame()
oversetInterval
.