跪拜 Guibai
← Back to the summary

requestAnimationFrame vs. setTimeout: Why Frame Sync Matters More Than You Think

Prerequisites: Essential Knowledge for Understanding requestAnimationFrame

Before learning requestAnimationFrame, you need to establish two foundational understandings: the nature of animation smoothness and the flaws of traditional approaches.

1. Monitor Refresh Rate and the Browser's "Frame"

2. The Core Pain Points of Traditional Timer-Based Animation

Early front-end animations were commonly implemented by periodically modifying element styles using setTimeout or setInterval, which have two inherent flaws:

requestAnimationFrame is a native API created precisely to solve the timing synchronization and performance optimization problems of animation.


1. Core Concepts of requestAnimationFrame

1. Definition

requestAnimationFrame (often abbreviated as rAF in the industry) is a browser-native API defined by the HTML5 standard. It is used to register a callback function with the browser, requesting the browser to execute that callback before the next screen repaint. It is a scheduling interface specifically optimized for visual animations. A simple explanation: it's like sending the browser a "pre-render execution" appointment, naturally aligning animation code with the screen's refresh rhythm and mechanically preventing stuttering caused by desynchronization.

2. Underlying Principles

The core essence of rAF is a callback scheduling mechanism deeply bound to the browser's rendering pipeline. The core logic has three points:

  1. Automatic Refresh Rate Alignment: The browser automatically schedules the callback execution frequency based on the current display's refresh rate, ensuring the callback executes at most once per frame, with no redundant calculations.
  2. Precise Execution Timing: The callback is fixed to execute at the point in each frame 'after JS task execution finishes and before style calculation begins'. Modifying element styles at this time will be processed for layout and painting in the current frame, avoiding cross-frame delays.
  3. Automatic Background Suspension: When a page tab is hidden or minimized, the browser automatically pauses the scheduling of rAF callbacks; it automatically resumes when the page becomes visible again, thus reducing performance overhead.

3. Concrete Example

Below is the standard implementation for moving an element horizontally at a constant speed using rAF, which is also the industry's common best practice:

const target = document.querySelector('.animate-box');
const totalDistance = 300; // Total movement distance 300px
const totalDuration = 2000; // Total animation duration 2000ms

let startTimestamp = null; // Animation start timestamp

// Animation function executed per frame
function step(currentTimestamp) {
  // Record start time on first execution
  if (!startTimestamp) startTimestamp = currentTimestamp;
  
  // Calculate elapsed time and animation progress
  const elapsed = currentTimestamp - startTimestamp;
  const progress = Math.min(elapsed / totalDuration, 1); // Progress clamped to 0~1
  
  // Update element position
  target.style.transform = `translateX(${progress * totalDistance}px)`;
  
  // If progress is not complete, register the next frame callback
  if (progress < 1) {
    requestAnimationFrame(step);
  }
}

// Start the animation
requestAnimationFrame(step);

The core characteristic of this approach is calculating animation progress based on time difference, rather than moving a fixed number of pixels per frame. Even if the device frame rate fluctuates, the total animation duration remains consistent, avoiding the problem of animations slowing down on slower devices.

4. Application Scenarios


2. Execution Timing of requestAnimationFrame

1. Definition

Execution timing refers to the specific stage within a single frame's rendering pipeline where the rAF callback resides, and its order of execution relative to other asynchronous tasks.

2. Underlying Principles

The standard pipeline for the browser to complete one frame of a screen, in order of execution, consists of the following stages:

  1. Process user input events (clicks, scrolls, keyboard input, etc.)
  2. Execute macro-tasks and micro-tasks (i.e., tasks in the JavaScript event loop queue)
  3. Execute the requestAnimationFrame callback queue
  4. Style Calculation (Style): Calculate the final effective CSS styles for all elements
  5. Layout (Layout, commonly known as reflow): Calculate the position and size of elements
  6. Paint (Paint, commonly known as repaint): Draw the visual content of elements onto their respective layers
  7. Compositing (Composite): Merge multiple layers into the final screen image and output it to the display

The rAF callback is positioned exactly after 'regular JS tasks' and before 'style calculation'. Modifying styles at this moment will be directly processed by the current frame's layout and paint processes, making it the optimal time to modify visual styles.

3. Concrete Example

Comparing the timing difference in applying style changes between a timer and rAF:

// Method A: setTimeout to modify styles
setTimeout(() => {
  target.style.width = '200px';
  // This change will likely miss the current frame and needs to wait for the next frame to render on screen
}, 0);

// Method B: rAF to modify styles
requestAnimationFrame(() => {
  target.style.width = '200px';
  // This change will be calculated during the current frame's layout stage and can be rendered in the same frame
});

setTimeout(fn, 0) places the callback into the next macro-task queue, executing later than the current frame's rendering process; whereas the rAF callback executes before the current frame renders, so style changes can be immediately processed by this rendering pipeline.

4. Application Scenarios


3. Selection Comparison with Traditional Timer-Based Animation

1. Definition

setTimeout / setInterval are general-purpose timing task APIs designed for delayed task execution; requestAnimationFrame is a dedicated rendering-optimized API designed for smooth visual updates. Their underlying scheduling mechanisms are completely different, and their applicable scenarios have clear boundaries.

2. Underlying Principles

3. Concrete Comparison

Comparison Dimension setTimeout / setInterval requestAnimationFrame
Execution Timing Scheduled by event loop, unrelated to rendering cycle Bound to rendering cycle, fixed execution before each frame repaint
Frame Rate Stability Unstable, prone to frame drops and skipping Stable, automatically aligns with monitor refresh rate
Background Page Behavior Continues execution, consumes system resources Automatically pauses, resumes automatically when page returns
Time Precision Millisecond level, has a minimum 4ms delay limitation Microsecond-level high-precision timestamp, no extra delay
Callback Accumulation High-frequency calls easily cause callback accumulation Multiple calls within a single frame are merged and executed, no accumulation
Design Purpose General-purpose asynchronous timing tasks Specifically for visual animation and rendering optimization

4. Application Scenarios


4. Core Features of requestAnimationFrame

1. Definition

rAF comes with three native features that are its core advantages as the premier choice for animation: automatic background suspension, a high-precision timestamp, and a cancellation mechanism.

2. Underlying Principles

3. Concrete Example

A standard encapsulation for starting and stopping an animation:

class AnimationController {
  constructor() {
    this.animationId = null;
  }

  start(animateFn) {
    const step = (timestamp) => {
      animateFn(timestamp);
      this.animationId = requestAnimationFrame(step);
    };
    this.animationId = requestAnimationFrame(step);
  }

  stop() {
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
      this.animationId = null;
    }
  }
}

Calling the stop() method in scenarios like component unmounting or modal closing can thoroughly clean up callbacks, preventing memory leaks and invalid executions.

4. Application Scenarios


5. Common Misconceptions

  1. Misconception: requestAnimationFrame always executes every 16.6ms

    Correction: The execution interval is determined by the monitor's refresh rate. A 60Hz screen is about 16.6ms, a 120Hz high-refresh screen is about 8.3ms; if the main thread is blocked by a long task, the frame interval will be further extended, and rAF will automatically postpone execution. It is not a fixed time interval.

  2. Misconception: Using requestAnimationFrame guarantees smooth animation

    Correction: rAF only solves the problem of execution timing synchronization. If the callback itself contains heavy computation or frequently triggers reflow and repaint, causing the total workload to exceed the single-frame time budget, the animation will still stutter. The core of smooth animation is controlling the total workload within a single frame, not simply using rAF.

  3. Misconception: requestAnimationFrame can only be used for animations

    Correction: The essence of rAF is 'execute before the next frame renders'. Besides animation, it can also be used for chunked rendering of large DOM batches, avoiding forced synchronous layouts, splitting long tasks, and other scenarios. Any logic that needs to align with the rendering rhythm can use it.

  4. Misconception: requestAnimationFrame and setTimeout(fn, 0) have similar effects

    Correction: Their execution timings are on completely different levels. setTimeout(fn, 0) places the callback into the next macro-task queue, executing after the current frame renders; the rAF callback executes before the current frame renders, much earlier than setTimeout 0, and with higher precision.


6. Practical Implementation Advice

Basic Usage Standards

Performance Optimization Advice

Boundary Compatibility Advice