For a long time, the web platform has offered JavaScript programmers a number of functions that allow them to asynchronously execute code after a certain time interval has elapsed, and to repeatedly execute a block of code asynchronously until you tell it to stop.
These functions are:
setInterval()
. Executes a specified block of code before the browser next repaints the display, allowing an animation to be run at a suitable framerate regardless of the environment it is being run in.The asynchronous code set up by these functions runs on the main thread (after their specified timer has elapsed).
It’s important to know that you can (and often will) run other code before a setTimeout()
call executes, or between iterations of setInterval()
. Depending on how processor-intensive these operations are, they can delay your async code even further, as any async code will execute only after the main thread is available. (In other words, when the stack is empty.) You will learn more on this matter as you progress through this article.
In any case, these functions are used for running constant animations and other background processing on a web site or application. In the following sections we will show you how they can be used.
As we said before, setTimeout() executes a particular block of code once after a specified time has elapsed. It takes the following parameters:
0
(or omit the value), the function will run as soon as possible. (See the note below on why it runs “as soon as possible” and not “immediately”.) More on why you might want to do this later.NOTE: The specified amount of time (or the delay) is not the guaranteed time to execution, but rather the minimum time to execution. The callbacks you pass to these functions cannot run until the stack on the main thread is empty.
As a consequence, code like setTimeout(fn, 0)
will execute as soon as the stack is empty, not immediately. If you execute code like setTimeout(fn, 0)
but then immediately after run a loop that counts from 1 to 10 billion, your callback will be executed after a few seconds.
In the following example, the browser will wait two seconds before executing the anonymous function, then will display the alert message (see it running live, and see the source code):
let myGreeting = setTimeout(function () { alert('Hello, Mr. Universe!'); }, 2000)
The functions you specify don’t have to be anonymous. You can give your function a name, and even define it somewhere else and pass a function reference to the setTimeout()
. The following two versions of the code snippet are equivalent to the first one:
// With a named function let myGreeting = setTimeout(function sayHi() { alert('Hello, Mr. Universe!'); }, 2000)
// With a function defined separately function sayHi() { alert('Hello Mr. Universe!'); } let myGreeting = setTimeout(sayHi, 2000);
That can be useful if you have a function that needs to be called both from a timeout and in response to an event, for example. But it can also just help keep your code tidy, especially if the timeout callback is more than a few lines of code.
setTimeout()
returns an identifier value that can be used to refer to the timeout later, such as when you want to stop it. See Clearing timeouts (below) to learn how to do that.
Any parameters that you want to pass to the function being run inside the setTimeout()
must be passed to it as additional parameters at the end of the list.
For example, you could refactor the previous function so that it will say hi to whatever person’s name is passed to it:
function sayHi(who) { alert(`Hello ${who}!`); }
Now, you can pass the name of the person into the setTimeout()
call as a third parameter:
let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');
Finally, if a timeout has been created, you can cancel it before the specified time has elapsed by calling clearTimeout(), passing it the identifier of the
setTimeout()
call as a parameter. So to cancel our above timeout, you’d do this:
clearTimeout(myGreeting);
Note: See greeter-app.html for a slightly more involved demo that allows you to set the name of the person to say hello to in a form, and cancel the greeting using a separate button (see the source code also).
setTimeout()
works perfectly when you need to run code once after a set period of time. But what happens when you need to run the code over and over again—for example, in the case of an animation?
This is where setInterval() comes in. This works in a very similar way to
setTimeout()
, except that the function you pass as the first parameter is executed repeatedly at no less than the number of milliseconds given by the second parameter apart, rather than once. You can also pass any parameters required by the function being executed as subsequent parameters of the setInterval()
call.
Let’s look at an example. The following function creates a new Date() object, extracts a time string out of it using
toLocaleTimeString(), and then displays it in the UI. It then runs the function once per second using
setInterval()
, creating the effect of a digital clock that updates once per second (see this live, and also see the source):
function displayTime() { let date = new Date(); let time = date.toLocaleTimeString(); document.getElementById('demo').textContent = time; } const createClock = setInterval(displayTime, 1000);
Just like setTimeout()
, setInterval()
returns an identifying value you can use later when you need to clear the interval.
setInterval()
keeps running a task forever, unless you do something about it. You’ll probably want a way to stop such tasks, otherwise you may end up getting errors when the browser can’t complete any further versions of the task, or if the animation being handled by the task has finished. You can do this the same way you stop timeouts — by passing the identifier returned by the setInterval()
call to the clearInterval() function:
const myInterval = setInterval(myFunction, 2000); clearInterval(myInterval);
There are a few things to keep in mind when working with setTimeout()
and setInterval()
. Let’s review these now.
There is another way to use setTimeout()
: you can call it recursively to run the same code repeatedly, instead of using setInterval()
.
The below example uses a recursive setTimeout()
to run the passed function every 100
milliseconds:
let i = 1; setTimeout(function run() { console.log(i); i++; setTimeout(run, 100); }, 100);
Compare the above example to the following one — this uses setInterval()
to accomplish the same effect:
let i = 1; setInterval(function run() { console.log(i); i++ }, 100);
setTimeout()
and setInterval()
differ?The difference between the two versions of the above code is a subtle one.
setTimeout()
guarantees the same delay between the executions. (For example, 100
ms in the above case.) The code will run, then wait 100
milliseconds before it runs again—so the interval will be the same, regardless of how long the code takes to run.setInterval()
does things somewhat differently. The interval you chose includes the time taken to execute the code you want to run in. Let’s say that the code takes 40
milliseconds to run — the interval then ends up being only 60
milliseconds.setTimeout()
recursively, each iteration can calculate a different delay before running the next iteration. In other words, the value of the second parameter can specify a different time in milliseconds to wait before running the code again.When your code has the potential to take longer to run than the time interval you’ve assigned, it’s better to use recursive setTimeout()
— this will keep the time interval constant between executions regardless of how long the code takes to execute, and you won’t get errors.
Using 0
as the value for setTimeout()
schedules the execution of the specified callback function as soon as possible but only after the main code thread has been run.
For instance, the code below (see it live) outputs an alert containing "Hello"
, then an alert containing "World"
as soon as you click OK on the first alert.
setTimeout(function () { alert('World'); }, 0); alert('Hello');
This can be useful in cases where you want to set a block of code to run as soon as all of the main thread has finished running — put it on the async event loop, so it will run straight afterwards.
clearTimeout()
and clearInterval()
both use the same list of entries to clear from. Interestingly enough, this means that you can use either method to clear a setTimeout()
or setInterval()
.
For consistency, you should use clearTimeout()
to clear setTimeout()
entries and clearInterval()
to clear setInterval()
entries. This will help to avoid confusion.
requestAnimationFrame() is a specialized looping function created for running animations efficiently in the browser. It is basically the modern version of
setInterval()
— it executes a specified block of code before the browser next repaints the display, allowing an animation to be run at a suitable frame rate regardless of the environment it is being run in.
It was created in response to perceived problems with setInterval()
, which for example doesn’t run at a frame rate optimized for the device, sometimes drops frames, continues to run even if the tab is not the active tab or the animation is scrolled off the page, etc.
So that’s it — all the essentials of async loops and intervals covered in one article. You’ll find these methods useful in a lot of situations, but take care not to overuse them! Because they still run on the main thread, heavy and intensive callbacks (especially those that manipulate the DOM) can really slow down a page if you’re not careful.