Have you ever written some JavaScript code, hit run, and then things just don’t happen in the order you thought they would.
Maybe a message logs too late, an action runs too soon, or something just disappears into the void.
It sucks right?
Well, the good news is there’s a solution to this called setTimeout
, and it’s fairly easy to use once you know how it works.
In this guide I’ll walk you through what setTimeout
really does, why it matters, and how to use it without getting tripped up. Once it clicks, a lot of other confusing stuff in JavaScript suddenly makes more sense.
Sidenote: If you want to learn more about JavaScript, be sure to check out Andrei’s Complete Web Developer course, as well as my Beginner and Advanced JavaScript projects:
We have everything you need to become a full-stack JavaScript developer in 2025.
Graduates from these programs are now working at Google, Tesla, Amazon, Apple, IBM, Uber, Facebook, Shopify + other top tech companies. There’s no reason why you couldn’t do the same!
With that out of the way, let’s get into this 5-minute tutorial.
setTimeout
is a built-in function that schedules a bit of code to run after a delay that’s measured in milliseconds. You can think of setTimeout
as telling JavaScript, “Hey, run this later.”
But what makes it powerful isn’t just the delay. It’s how that delay fits into JavaScript’s asynchronous nature.
This is because JavaScript is single-threaded. It does one thing at a time, line by line.
The problem of course is that sometimes you don’t want everything to happen immediately. Maybe you want to show a loading spinner, delay a notification, or give the UI a chance to update before doing something heavy.
And that’s where setTimeout
comes in to save the day.
For example
Here’s the basic syntax:
setTimeout(callback, delay);
Callback
refers to a function you want to run laterDelay
is simply how long to wait (in milliseconds) before running itSo if you write:
setTimeout(() => {
console.log("This runs after 2 seconds");
}, 2000);
JavaScript will wait at least 2 seconds before logging the message.
What’s important to understand is that it doesn’t pause the whole program, it just sets that function aside and keeps going, and lets the rest of your code keep executing while it waits in the background.
For example
Take a look at this code block with a delay added:
console.log("One");
setTimeout(() => {
console.log("Two");
}, 0);
console.log("Three");
You might think it would print “One, Two, Three”. But it actually prints:
One
Three
Two
Why?
Well, this is because even a setTimeout
with 0
delay doesn’t run immediately. It just says, “Run this after the current stack of code is done.” So JavaScript finishes everything it’s doing first, then checks the timeout queue.
That’s part of what’s called the event loop, and setTimeout
is one of the simplest ways to see it in action.
This pattern comes up more than you’d think.
You might use setTimeout
to:
For example
Let’s say that you have a button that when clicked, should show a confirmation message for just a moment:
button.addEventListener("click", () => {
message.innerText = "Saved!";
setTimeout(() => {
message.innerText = "";
}, 1500);
});
That’s the kind of thing setTimeout
was made for, where it lets you show something briefly, then disappear later, without blocking the rest of your app.
setTimeout
isn’t a performance booster. It doesn’t make your site load faster or fix your Core Web Vitals score, but it can make things feel smoother.
How?
Well, simply because it gives the browser a chance to paint the UI before running heavier logic. So instead of everything happening at once and freezing the screen, your app feels more responsive. (It’s more of a user experience tool than a true speed upgrade).
If you're looking to really boost performance, you'll want to explore things like lazy loading, requestIdleCallback
, or Web Workers. I’ll cover those in a future guide.
But for now, let’s look at something just as important, which is how to actually stop a setTimeout
once you’ve started it.
Sometimes you schedule something with setTimeout
, but then realize you don’t want it to run after all. Maybe the user navigated away, clicked a different button, or some other condition changed.
Luckily, JavaScript gives you a way to cancel a timeout before it runs with clearTimeout
.
Here’s how it works.
When you call setTimeout
, it returns an ID, which is basically a reference to the scheduled task. You can store that ID in a variable and then use clearTimeout(id)
to cancel it.
For example
const timeoutId = setTimeout(() => {
console.log("This will never run");
}, 3000);
clearTimeout(timeoutId);
In this case, the setTimeout
was scheduled for 3 seconds, but it’s cancelled immediately after, and the callback never runs.
This is especially useful when you’re dealing with interactive elements.
For example
Maybe you want to show a tooltip when the user hovers over something, but if they move their mouse away quickly you don’t want that tooltip to still appear.
let tooltipTimeout;
button.addEventListener("mouseenter", () => {
tooltipTimeout = setTimeout(() => {
showTooltip();
}, 500);
});
button.addEventListener("mouseleave", () => {
clearTimeout(tooltipTimeout);
});
Here, the tooltip only shows if the user hovers for at least 500ms. If they leave earlier, the setTimeout
is cancelled and the tooltip never appears. That gives you much finer control over timed behavior.
Another common use case is for cancelling delayed actions during form validation or multi-step flows, where you don’t want multiple timeouts stacking up if the user keeps interacting.
Just remember:
setTimeout
always returns a numeric ID (in browsers)clearTimeout(id)
only works if the ID still existsclearTimeout
does nothing—but also doesn’t throw an errorHandy right?
This makes clearTimeout
a safe tool to use even if you’re not 100% sure whether the timeout has already fired.
Although setTimeout
is simple on the surface, it's one of those tools that trips people up when they don’t fully understand how JavaScript handles timing.
So let’s walk through some of the most common mistakes and how to avoid them.
One of the most common errors is accidentally calling the function instead of passing it as a reference.
For example
Here’s what that looks like:
setTimeout(doSomething(), 2000); // ❌ Wrong
Notice that this doesn’t wait 2 seconds. Instead, it runs doSomething()
immediately, and then passes whatever it returns into setTimeout
, which does nothing useful.
The solution is to pass the function reference, or wrap it in an arrow function, like so:
setTimeout(doSomething, 2000); // ✅ Right
setTimeout(() => doSomething(), 2000); // ✅ Also fine
If you're not sure whether you're calling the function or passing it, a quick rule of thumb is that if you see parentheses after the name, you’re calling it.
If you ever need to cancel a timeout, you must store its ID. Otherwise, there’s no way to reference it later with clearTimeout
.
For example
This is wrong:
setTimeout(() => {
console.log("Will run, and can’t be cancelled");
}, 3000);
The solution is to always store the timeout if you might cancel it:
const timeoutId = setTimeout(() => {
console.log("Maybe cancelled");
}, 3000);
// later...
clearTimeout(timeoutId);
Even if you’re not sure you’ll cancel it, it’s a good habit to keep the ID around, especially in interactive apps.
Like I said earlier, some beginners think setTimeout
waits before moving on but it doesn’t. It just sets a timer for that part of the code, but keeps going with the rest.
setTimeout(() => {
console.log("Done waiting");
}, 2000);
console.log("Still running…");
This becomes a bigger deal when you expect things to happen in a specific order but don’t account for the delay. This is why you need to think of setTimeout
as a way to schedule some code for later, not as an actual “pause” of the entire code.
It’s common to assume you can use setTimeout
in a loop to space things out, but that’s not how it works.
For example
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
At first glance, you might expect this to log one number per second: 0
, then 1
, then 2
, and so on, but that’s not what happens.
Instead, all five setTimeout
calls are scheduled at the same time with the same delay, so they all run together after one second.
This means you’ll actually see these numbers printed at once, and not one per second.
0
1
2
3
4
If you want to delay between each step, you’ll need to use a recursive setTimeout
instead:
let i = 0;
function logNext() {
if (i < 5) {
console.log(i);
i++;
setTimeout(logNext, 1000);
}
}
logNext();
This way, each call waits a full second before logging the next number. You get one log per second, just as you'd expect from a loop with pauses in between.
Arrow functions are convenient, especially for short bits of code. However, when you're using setTimeout
in more complex or long-lived code, relying only on anonymous functions can make your code harder to read, debug, or cancel.
For example
const timeoutId = setTimeout(() => {
message.innerText = "";
}, 1500);
This works fine, but imagine trying to cancel or reuse that logic elsewhere. It would be overly complex because it’s all buried inside the timeout.
The solution is to use named functions when the logic is more than a one-liner or when you might reuse or cancel the timeout:
function hideMessage() {
message.innerText = "";
}
const timeoutId = setTimeout(hideMessage, 1500);
Named functions make it easier to cancel a timeout later, move the logic into its own file, or even just make your code easier to scan and understand.
As you can see, learning setTimeout
is more than just figuring out a timer. It’s your first real look at how JavaScript handles timing, defers work, and reacts over time.
Once you see how it fits into the bigger picture of asynchronous behavior, a lot of other things start to click. (It’s the kind of tool that teaches you how the language thinks which opens up a lot of aha moments).
The best way to understand a new concept though is to try it out for yourself. So go ahead and use setTimeout
in one of your own projects. Nothing too fancy, delay a message, cancel one, or create a timed sequence.
You’ll learn way more by playing with it than by just reading about it.
Remember, if you want to learn more about JavaScript, be sure to check out Andrei’s Complete Web Developer course, as well as my Beginner and Advanced JavaScript projects:
We have everything you need to become a full-stack JavaScript developer in 2025, build a portfolio, and get hired.
In fact, graduates from these programs are now working at Google, Tesla, Amazon, Apple, IBM, Uber, Facebook, Shopify + other top tech companies. There’s no reason why you couldn’t do the same!
As a ZTM member, you get access to all these courses and more in a single membership.
Plus, once you join, you'll have the opportunity to ask questions in our private Discord community from me, other students and other working tech professionals, as well as access to every other course in our library!