Introduction to JavaScript Timers
Timers are a crucial tool that allow scheduling code to execute in the future after a set time delay. JavaScript provides in-built timer functions that handle timing behind the scenes to enable common use cases like timeouts, intervals, delays, and more.
The ability to delay or periodically execute code is required in most web and Node.js applications. Some common use cases for timers include:
- Running code after a delay or timeout
- Repeating code continuously at an interval
- Debouncing input handlers
- Scheduling future tasks
- Updating variable values on an interval
- Implementing countdowns or clocks
- Benchmarking function execution time
- Adding delays between operations
Traditionally, timers involved complex logic to capture timestamps and calculate future execution. JavaScript abstracts this complexity by providing simple timer functions as part of the language and runtime.
In this detailed guide, we will learn about the different types of JavaScript timers, how they work under the hood, and how to leverage them to schedule asynchronous code execution elegantly.
setTimeout()
The setTimeout()
function allows scheduling a callback function to execute after a minimum delay.
Syntax
setTimeout(callback, delayInMs)
callback
– Function to executedelayInMs
– Delay in milliseconds
Example
function greeting() {
console.log('Hello World')
}
setTimeout(greeting, 1000) // Hello World after 1s
The callback greeting()
is invoked asynchronously after 1 second.
setTimeout()
returns a timer id that can be used to reference the timer.
Timers scheduled via setTimeout()
are not guaranteed to run exactly after the delay specified due to operations like scrolling that occupy the main thread. The minimum delay is respected.
Canceling Timers
Timers can be canceled before they execute using their id:
const id = setTimeout(() => {
console.log('Cancelled')
}, 1000)
clearTimeout(id) // Timer cleared
Calling clearTimeout(id)
cancels the timer if it has not already executed.
setInterval()
To repeatedly execute code on an interval, the setInterval()
function can be used:
Syntax
setInterval(callback, delay)
It will continuously invoke the callback after the interval elapses.
Example
setInterval(() => {
console.log('Repeating every 1s')
}, 1000)
This logs the message every 1 second recurrently.
Like setTimeout()
, it returns an id that can be used to stop the interval with clearInterval()
.
setImmediate()
setImmediate()
schedules the callback to execute right after the current poll phase completes and before the next event loop iteration starts.
The difference from setTimeout()
with a delay of 0 ms is that setImmediate()
attempts to execute at the end of the current event loop.
Syntax
setImmediate(callback)
Example
console.log('A')
setImmediate(() => {
console.log('C')
})
console.log('B')
// Logs:
// A
// B
// C
This demonstrates setImmediate()
executing between two iterations of the event loop.
requestAnimationFrame()
Animation logic like moving elements on a page needs to execute just before the browser paints a new frame. requestAnimationFrame()
schedules a callback for this optimal time.
Syntax
requestAnimationFrame(callback)
The callback executes right before the next repaint.
Example
let x = 0
function animate() {
x++
console.log(x)
requestAnimationFrame(animate)
}
requestAnimationFrame(animate)
This schedules animate()
to run recursively and logs the incrementing counter.
The callback time aligns with the display refresh rate for smooth animations.
Multiple requestAnimationFrame()
calls are batched to fire in a single callback to avoid over-rendering.
How Timers Work Internally
Now that we have seen the common timer functions, let’s briefly understand how they work under the hood:
- Timers use the ECMAScript specification Timer interface internally.
- The timing heartbeat provides a steady tick used to check if timers should fire.
- Active timers are organized in a priority heap queue based on their duration.
- On each tick, the timer queue is checked if any timer callback is ready to be fired.
- The callback is then executed and removed from the queue.
- The JS environment keeps track of handles to clear or cancel timers as needed.
The Queue data structure and scheduling algorithm ensure optimal timer execution performance.
Use Cases for JavaScript Timers
Let’s explore some practical use cases where timer functions come in handy:
Executing Code After a Delay
Run code after a delay of 2 seconds:
setTimeout(() => {
// Runs after 2 seconds
}, 2000)
This avoids blocking code while waiting.
Debouncing Rapid Events
Debounce rapid succession of events like typing:
let timer
function search(term) {
clearTimeout(timer)
timer = setTimeout(() => {
// Actual search logic
}, 500)
}
Frequent callbacks are consolidated into a single execution.
Scheduling Future Tasks
Scheduling an important task to execute after 1 hour:
setTimeout(() => {
sendEmailReminder()
}, 3600000) // 1 hour
No need to keep rechecking the time ourselves.
Repeating Operations
Refresh token values every 15 minutes:
setInterval(() => {
refreshToken()
}, 15 * 60 * 1000)
Intervals are useful for recurrent execution.
These show just some examples. There are many other timer use cases like throttling, polling, etc.
Common Pitfalls and Best Practices
Here are some tips to use timers effectively:
- Return values from timer callbacks instead of depending on external state which can change.
- Pass needed context to callbacks as arguments instead of using closures which hold references.
- Use
clearTimeout()/clearInterval()
if timer is no longer needed. - Avoid new timers inside callbacks as it can lead to unpredictable nested execution.
- Do not use
setInterval()
when precise scheduling is needed. UsesetTimeout()
recursively. - Adjust for drift in
setInterval()
for operations that need to run at fixed intervals. - Use timeouts wisely on user events – they can impact perceived performance.
Understanding the common pitfalls helps build robust applications using timers.
Alternatives to JavaScript Timers
While the built-in timer functions cover most use cases, here are some alternatives:
- RxJS Timer – Provides timer observables that emit values.
- XHR Polling – Use AJAX requests to poll the server.
- Web Workers – Offload timers to run in a separate thread.
- requestIdleCallback() – Execute non-critical background and cleanup tasks.
Each have their own advantages in specific situations.
Conclusion
Timers are a fundamental capability required in most JavaScript code. The language provides simple yet powerful functions like setTimeout
, setInterval
, requestAnimationFrame
etc. to fulfill common timing needs.
Using the right timers judiciously helps build robust applications. Timers allow coordinating and reacting to events elegantly in asynchronous JavaScript environments. They abstract away complex factors like clock drift and provide optimal callback scheduling.
Understanding how JavaScript timers work under the hood and their strengths and weaknesses enables using them effectively. Timers help achieve complex timing and coordination imperatives with just a few lines of clean code.
Frequently Asked Questions
Here are some common questions about JavaScript timers:
What is the difference between setTimeout and setImmediate?
setTimeout()
schedules execution after a minimum time delay. setImmediate()
attempts to execute at the end of the current event loop iteration.
When should I use requestAnimationFrame() vs setInterval()?
Use requestAnimationFrame()
for visual animations to harness frame refresh syncing. Use setInterval()
for general recurring code execution.
How many setTimeout() timers can be running at once?
There is no set limit. But too many can impact performance. Bundle timers when possible and cancel obsolete ones.
Can clearTimeout() stop a timer before it starts?
Yes, as long as the scheduled callback has not executed yet, calling clearTimeout()
for that timer will cancel it.
Are JavaScript timers accurate?
Timers may drift slightly over time. Critical operations like tests