Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme


Color

Background


Done

Table of Contents

Timers in JavaScript

by kirupa   |   filed under JavaScript 101

By default, our code runs synchronously. That is a fancy of way of saying that when a statement needs to execute, it executes immediately. There are no ands, ifs, or buts about it. The concept of delaying execution or deferring work to later isn't a part of JavaScript's default behavior. We kinda sorta saw this when looking at loops earlier. The loop runs at lightning speed with no delay between each iteration. That is great for making quick calculations, but that isn't great if we want to make an update at a more measured (aka slower!) pace.

All of this doesn't mean the ability to stop work from running instantaneously doesn't exist! If we swerve just slightly off the main road, there are three functions that allow us to mostly do just that (and more) - setTimeout, setInterval, and requestAnimationFrame. In this article, we will look at what each of these functions do.

Onwards!

Delaying with setTimeout

The setTimeout function allows us to delay executing some code. The way we use it is quite nice. This function allows us to specify what code to execute and how many milliseconds to wait before the code we specified executes. Putting that into JavaScript, it will look something like this:

let timeoutID = setTimeout(someFunction, delayInMilliseconds);

Going a bit more example-ish, if we wanted to call a function called showAlert after 5 seconds, the setTimeout declaration would look as follows:

function showAlert() {
  alert("moo!");
}

let timeoutID = setTimeout(showAlert, 5000);

Cool, right? Now, let's talk about something less interesting that we need to cover for completeness. That something has to do with the timeID variable that is initialized to our setTimeout function. It isn't there by accident. If we ever wanted to access this setTimeout timer again, we need a way to reference it. By associating a variable with our setTimeout declaration, we can easily accomplish that.

Now, you may be wondering why we would ever want to reference a timer once we've created it. There aren't too many reasons. The only reason I can think of would be to cancel the timer. For setTimeout, that is conveniently accomplished using the clearTimeout function and passing the timeout ID as the argument:

clearTimeout(timeoutID);

If you are never planning on canceling your timer, you can just use setTimeout directly without having it be part of the variable initialization.

Anyway, moving past the technical details on how to use setTimeout, let's talk about when we would commonly use it in the real world. Two words: UI development. When we are doing UI development, deferring some action to a later time is unusually common. Here are some examples that I ran into just in the past month:

  1. A menu slides in, and after a few seconds of the user no longer playing with the menu, the menu slides away.
  2. You have a long running operation that is unable to complete, and a setTimeout function interrupts that operation to return control back to the user.
  3. My favorite (and one that I wrote a tutorial about as is well) is where you use the setTimeout function to detect whether users are inactive or idle!

If you do a search for setTimeout on this site or google, you'll see many more real-world cases where setTimeout proves very useful.

Looping with setInterval

The next timer function we are going to look at is setInterval. The setInterval function is similar to setTimeout in that it also allows you to execute code after a specified amount of time. What makes it different is that it doesn't just execute the code once. It keeps on executing the code in a loop forever.

Here is how you would use the setInterval function:

let intervalID = setInterval(someFunction, delayInMilliseconds);

Except for the function name, the way you use setInterval is even identical to setTimeout. The first argument specifies the inline code or function you would like to execute. The second argument specifies how long to wait before your code loops again. You can also optionally initialize the setInterval function to a variable to store an interval ID - an ID that you can later use to do exciting things like cancel the looping. Yay!!!

OK! Now that we've seen all that, here is an example of this code at work for looping a function called drawText with a delay of 2 seconds between each loop:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Show me some text!</title>
</head>

<body>
  <script>
    let thingToPrint = "";
    
    function drawText() {
      thingToPrint += "#";
      document.writeln(thingToPrint);
    }

    setInterval(drawText, 2000);
  </script>
</body>

</html>

If we wish to cancel the looping, we can use the appropriately named clearInterval function:

clearInterval(intervalID);

Its usage is similar to its clearTimeout equivalent. We pass in the ID of the setInterval timer instance that we optionally retrieved while setting up our setInterval in the first place.

In real life, setInterval was the primary function you had for the longest time for creating animations in JavaScript. To get 30 or 60 frames a second, you would do something as follows by playing with the delay time value:


// 1000 divided 60 is the millisecond value for 60fps
setInterval(moveCircles, 1000 / 60);

To see setInterval in action in some other realistic examples on this site itself, check out the bottom of the Creating a Sweet Content Slider article as well as the Creating an Analog Clock article. They both feature setInterval quite prominently!

Animating Smoothly with requestAnimationFrame

Now, we get to one of my favorite functions ever: requestAnimationFrame. The requestAnimationFrame function is all about synchronizing your code with a browser repaint event. What this means is this: your browser is busy juggling a billion different things at any given time. These things include fiddling with layout, reacting to page scrolls, listening for mouse clicks, displaying the result of keyboard taps, executing JavaScript, loading resources, and more. At the same time your browser is doing all of this, it is also redrawing the screen at 60 frames per second...or at least trying its very best to.

When you have code that is intended to animate something to the screen, you want to ensure your animation code runs properly without getting lost in the shuffle of everything else your browser is doing. Using the setInterval technique mentioned earlier doesn't guarantee that frames won't get dropped when the browser is busy optimizing for other things. To avoid your animation code from being treated like any other generic JavaScript, you have the requestAnimationFrame function. This function gets special treatment by the browser. This special treatment allows it to time its execution perfectly to avoid dropped frames, avoid unnecessary work, and generally steer clear of other side effects that plague other looping solutions.

The way we use this function starts off a bit similar to setTimeout and setInterval:

let requestID = requestAnimationFrame(someFunction);

The only real difference is that we don't specify a duration value. The duration is automatically calculated based on the current frame rate, whether the current tab is active or not, whether your device is running on battery or not, and a whole host of other factors that go beyond what we can control or understand.

Anyway, this usage of the requestAnimationFrame function is merely the textbook version. In real life, you'll rarely make a single call to requestAnimationFrame like this. Key to all animations created in JavaScript is an animation loop, and it is this loop that we want to throw requestAnimationFrame at. The result of that throw looks something as follows:

function animationLoop() {
  // animation-related code

  requestAnimationFrame(animationLoop)
}

// start off our animation loop!
animationLoop();

Notice that our requestAnimationFrame calls the animationLoop function fully from within the animationLoop function itself. That isn't a bug in the code. While this kind of circular referencing would almost guarantee a frozen/hung browser, requestAnimationFrame's implementation avoids that. Instead, it ensures the animationLoop function is called just the right amount of times needed to ensure things get drawn to the screen to create smooth and fluid animations. It does so without freezing the rest of your application functionality up.

To learn more about requestAnimationFrame and its primary use in creating awesome animations, you should check out all the content in the Animations in JavaScript section. In that section, I also dive deeper into requestAnimationFrame beyond the highlights we've looked at here.

Conclusion

If you think that timers fall under a more niche category compared to some of the other more essential things like if/else statements and loops, you would probably be right in thinking that. You can build many awesome apps without ever having to rely on setTimeout, setInterval, or requestAnimationFrame. That doesn't mean it isn't essential to know about them, though. There will be a time when you'll need to delay when your code executes, loop your code continuously, or create a sweet animation using JavaScript. When that time arrives, you'll be prepared...or at least know what to Google for.

Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!

Kirupa's signature!

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!

SUBSCRIBE NOW

Creating engaging and entertaining content for designers and developers since 1998.

Follow:

Popular

Loose Ends

:: Copyright KIRUPA 2024 //--