Asynchronous JavaScript
setTimeout, setInterval, requestAnimationFrame
Master scheduling timing in JavaScript. Contrast setTimeout, setInterval, and requestAnimationFrame in terms of execution loop alignment and screen refresh sync.
1. Introduction
JavaScript provides timers to schedule tasks to run in the future: setTimeout (one-time execution), setInterval (recurring execution), and requestAnimationFrame (execution aligned with screen refresh cycles).
2. Why It Matters
Using the wrong timing method can cause problems. For example, using setInterval for animations can cause stuttering, while using setTimeout for UI changes can cause visual tearing if the code runs out of sync with the screen's refresh rate.
3. Real-World Analogy
Think of a Theater Production Stage Manager:
- setTimeout (Timed Cue): "Wait 5 seconds, then drop the curtain once." The action is scheduled to run after a delay.
- setInterval (Timed Repeater): "Flash the strobe light every 2 seconds, indefinitely." The action runs repeatedly at set intervals.
- requestAnimationFrame (Conductor Sync): "Only step onto the stage when the spotlights are aligned and the cameras are recording a new frame." The action is synchronized with the production's visual layout, ensuring a smooth presentation.
4. Timers and Animation Scheduling
Let's explore the syntax and behavior of the three APIs:
1. setTimeout & setInterval:
Standard macro-scheduling timers. They are not guaranteed to run at exact times; they are delayed if the Call Stack is busy.
2. requestAnimationFrame (rAF):
Schedules callbacks to run immediately before the browser performs a repaint cycle (usually 60 times per second, or matching the screen's refresh rate). Unlike timers, requestAnimationFrame automatically pauses when the browser tab is inactive, saving battery and CPU cycles.
5. Comparison Summary
| API | Execution Alignment | Stops when tab is inactive? | Primary Use Case |
|---|---|---|---|
setTimeout |
Callback Queue (Event Loop tick) | No (throttled to 1s in some browsers) | One-time deferred tasks |
setInterval |
Callback Queue (Event Loop tick) | No (throttled to 1s) | Recurring non-UI updates (e.g. data polling) |
requestAnimationFrame |
Repaint Queue (Screen Refresh rate) | Yes (automatically paused) | Smooth UI animations and rendering loops |
6. Practical Example
This script demonstrates implementing a countdown timer that stops automatically when it reaches zero:
7. Common Mistakes
- Overlapping setInterval ticks: If the callback execution takes longer than the interval delay, the callbacks queue up, executing back-to-back with no delay. Use recursive
setTimeoutinstead to ensure there is a constant delay between executions.
8. Quick Quiz
Q1: Which scheduling API automatically pauses execution when the browser tab is hidden or minimized?
A) setInterval
B) requestAnimationFrame
Answer: B — requestAnimationFrame pauses execution when the tab is inactive to optimize performance and save battery power.
9. Scenario-Based Challenge
The Smooth Slider Component:
You are building an image slider animation. If you use setInterval(move, 16), users experience visual stuttering on 120Hz screens. Explain why switching to requestAnimationFrame resolves the stuttering issue across different screens.
10. Debugging Exercise
Identify the nesting stack overflow bug in this recursive timer:
function animateBox() { const el = document.getElementById('item'); el.style.left = (parseInt(el.style.left) || 0) + 1 + 'px';
// Bug: calling requestAnimationFrame requires passing the function reference! requestAnimationFrame(animateBox()); // crashes with stack overflow! }
View Solution
Diagnosis: The code invokes animateBox() immediately during the requestAnimationFrame registration call, causing infinite synchronous recursion and a stack overflow.
Fix: Pass the function reference itself without invoking it:
requestAnimationFrame(animateBox); // Pass function reference
11. Interview Questions
🟢 Q1: Why is requestAnimationFrame better than setTimeout or setInterval for rendering UI animations?
Answer:
1. Screen Refresh Sync: requestAnimationFrame is synchronized with the browser's repaint cycle, ensuring animations match the screen's refresh rate (e.g. 60Hz, 90Hz, 120Hz) without skipped frames.
2. Throttling: The browser automatically pauses requestAnimationFrame animations when the tab is inactive, reducing CPU usage and saving battery. Timers continue to run in the background.
3. Rendering Optimization: The browser groups all requestAnimationFrame DOM updates into a single repaint flow, reducing layout thrashing.
12. Production Considerations
- • Cleanup Resources: Always clean up active timers (using
clearTimeoutorclearInterval) in component teardown phases (like React'suseEffectcleanup returns) to prevent memory leaks and unexpected background activity.