Timers in JavaScript

by kirupa   |   27 October 2015

By default, your code runs synchronously. That is a fancy of way of saying that when a statement needs to execute, it executes immediately. There are no and, ifs, or buts about it. The concept of delaying execution or deferring work to later isn't a part of JavaScript's default behavior. That doesn't mean the ability to delay work to a later time doesn't exist! If you swerve just slightly off the main road, there are three functions that allow you to mostly do just that (and more) - setTimeout, setInterval, and requestAnimationFrame.

In this article, we will look at what each of these function do and other good stuff that comes along with learning more about JavaScript basic API surface.

Onwards!

OMG! A JavaScript Book Written by Kirupa?!!

To kick your JavaScript skills into outer space, everything you see here and more (with all its casual clarity!) is available in both paperback and digital editions.

BUY ON AMAZON

Meet the Three Timers

Like you saw a few pixels ago, the main suspects of this tutorial are going to be the setTimeout, setInterval, and requestAnimationFrame functions. In the following sections, let's look at each of these functions in greater detail and figure out a reason for their existence.

Delaying with setTimeout

The setTimeout function allows you to delay executing some code. The way you use it is pretty simple. This function allows you to specify what code to execute and how many milliseconds to wait before the code you specified executes.

Putting that into JavaScript, it will look something like this:

var timeID = window.setTimeout(someFunction, delayInMilliseconds);	

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

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

var timeID = window.setTimeout(showAlert, 5000);

Pretty simple, right? Now, let's talk about something less interesting that I cover just 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 you ever wanted to access this setTimeout timer again, you 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:

window.clearTimeout(timeID);

If you are never planning on cancelling your timer, you can just use window.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 you would commonly use it in the real world. As you will find out eventually, especially if you 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:

var intervalID = window.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:

function drawText() {
  document.querySelector("p").textContent += "#\n";
}

var intervalID = window.setInterval(drawText, 2000);

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

window.clearInterval(intervalID);

Its usage is similar to its clearTimeout equivalent. You pass in the ID of the setInterval timer instance that you optionally retrieved while setting up your 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:

// 1000 divided 60 is the millisecond value for 60fps
window.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 pretty simple. Your browser is busy juggling a billion different things at any given time - 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. To simplify even further, requestAnimationFrame is what you use if you want to draw and animate things to the screen.

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 you use this function starts off a bit similar to setTimeout and setInterval:

var requestID = requestAnimationFrame(someFunction);

The only real difference is that you 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 hang, 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.

If you have a question about this or any other topic, the easiest thing is to drop by our forums where a bunch of the friendliest people you'll ever run into will be happy to help you out!

THE KIRUPA NEWSLETTER

Get cool tips, tricks, selfies, and more...personally hand-delivered to your inbox!

( View past issues for an idea of what you've been missing out on all this time! )

GOT A QUESTION?

HOT FORUM TOPICS

Serving you freshly baked content since 1998!

Killer hosting by (mt) mediatemple

Facebook Twitter Youtube Pinterest Instagram Github
BACK TO TOP
new books - yay!!!