Back

requestAnimationFrame vs setTimeout : Quand utiliser chacune des méthodes

requestAnimationFrame vs setTimeout : Quand utiliser chacune des méthodes

Lors de la création d’animations fluides ou de la planification de tâches en JavaScript, le choix entre requestAnimationFrame et setTimeout peut considérablement impacter les performances de votre application. Bien que ces deux méthodes planifient l’exécution de fonctions, elles servent des objectifs fondamentalement différents et fonctionnent selon des mécanismes de temporisation distincts.

Points clés à retenir

  • requestAnimationFrame se synchronise avec la fréquence de rafraîchissement d’affichage du navigateur pour des mises à jour visuelles fluides
  • setTimeout fournit une temporisation polyvalente pour les tâches non-visuelles et les opérations en arrière-plan
  • Utiliser la mauvaise méthode peut causer des problèmes de performance, une consommation excessive de batterie et une mauvaise expérience utilisateur
  • requestAnimationFrame se met automatiquement en pause dans les onglets inactifs, tandis que setTimeout continue de s’exécuter

Comprendre les différences fondamentales

setTimeout : Le minuteur polyvalent

setTimeout exécute une fonction après un délai spécifié en millisecondes. C’est un minuteur simple et prévisible qui fonctionne indépendamment du cycle de rendu du navigateur.

// Execute after 1 second
setTimeout(() => {
  console.log('One second has passed');
}, 1000);

// With parameters
setTimeout((message) => {
  console.log(message);
}, 2000, 'Hello after 2 seconds');

La caractéristique clé de setTimeout est son exécution basée sur la file de tâches. Lorsque le délai expire, le callback rejoint la file de tâches de la boucle d’événements JavaScript, en concurrence avec d’autres tâches pour le temps d’exécution.

requestAnimationFrame : Le spécialiste de l’animation

requestAnimationFrame (rAF) se synchronise avec le cycle de repeint du navigateur, s’exécutant généralement à 60 images par seconde sur la plupart des écrans. Il est spécifiquement conçu pour les mises à jour visuelles.

function animate(timestamp) {
  // Update animation based on timestamp
  const element = document.getElementById('animated-element');
  element.style.transform = `translateX(${timestamp / 10}px)`;
  
  // Continue animation
  if (timestamp < 5000) {
    requestAnimationFrame(animate);
  }
}

requestAnimationFrame(animate);

Considérations de performance et de temporisation

Intégration au pipeline de rendu du navigateur

L’avantage fondamental de requestAnimationFrame réside dans son intégration au pipeline de rendu du navigateur. Tandis que setTimeout se déclenche dès que son délai expire, les callbacks de requestAnimationFrame s’exécutent juste avant que le navigateur calcule la mise en page et dessine les pixels à l’écran.

Cette synchronisation élimine les problèmes d’animation courants :

  • Déchirement d’écran : Artefacts visuels provenant de mises à jour en milieu d’image
  • Saccades : Temporisation irrégulière des images causant des mouvements hachés
  • Rendus gaspillés : Dessiner des images qui ne s’affichent jamais

Efficacité des ressources

requestAnimationFrame se met automatiquement en pause lorsque l’onglet du navigateur devient inactif, économisant les cycles CPU et la batterie. setTimeout continue de s’exécuter dans les onglets en arrière-plan, bien que les navigateurs puissent le limiter à une fois par seconde après environ une minute.

// Battery-efficient animation loop
function gameLoop(timestamp) {
  updatePhysics(timestamp);
  renderGraphics();
  requestAnimationFrame(gameLoop);
}

// Inefficient approach with setTimeout
function inefficientLoop() {
  updatePhysics();
  renderGraphics();
  setTimeout(inefficientLoop, 16); // Attempting 60fps
}

Cas d’usage pratiques

Quand utiliser requestAnimationFrame

Utilisez requestAnimationFrame pour toutes les mises à jour visuelles :

  • Animations de propriétés CSS
  • Opérations de dessin sur canvas
  • Rendu WebGL
  • Mises à jour de position DOM
  • Indicateurs de progression pendant les animations
// Smooth scroll implementation
function smoothScrollTo(targetY, duration) {
  const startY = window.scrollY;
  const distance = targetY - startY;
  const startTime = performance.now();
  
  function scroll(currentTime) {
    const elapsed = currentTime - startTime;
    const progress = Math.min(elapsed / duration, 1);
    
    // Easing function for smoother motion
    const easeInOutQuad = progress * (2 - progress);
    
    window.scrollTo(0, startY + distance * easeInOutQuad);
    
    if (progress < 1) {
      requestAnimationFrame(scroll);
    }
  }
  
  requestAnimationFrame(scroll);
}

Quand utiliser setTimeout

Choisissez setTimeout pour les tâches non-visuelles :

  • Appels d’API différés
  • Anti-rebond (debouncing) des saisies utilisateur
  • Opérations de polling
  • Tâches planifiées en arrière-plan
  • Délais ponctuels
// Debounced search
let searchTimeout;
function handleSearchInput(query) {
  clearTimeout(searchTimeout);
  searchTimeout = setTimeout(() => {
    performSearch(query);
  }, 300);
}

// Retry logic with exponential backoff
function fetchWithRetry(url, attempts = 3, delay = 1000) {
  return fetch(url).catch(error => {
    if (attempts > 1) {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve(fetchWithRetry(url, attempts - 1, delay * 2));
        }, delay);
      });
    }
    throw error;
  });
}

Pièges courants et solutions

Suppositions sur la fréquence d’images

Ne supposez jamais une fréquence d’images fixe avec requestAnimationFrame. Différents écrans se rafraîchissent à différentes fréquences (60Hz, 120Hz, 144Hz). Utilisez toujours le paramètre timestamp pour les animations basées sur le temps :

let lastTime = 0;
function animate(currentTime) {
  const deltaTime = currentTime - lastTime;
  lastTime = currentTime;
  
  const element = document.getElementById('moving-element');
  const currentLeft = parseFloat(element.style.left) || 0;
  
  // Move 100 pixels per second regardless of frame rate
  const pixelsPerMs = 100 / 1000;
  element.style.left = `${currentLeft + pixelsPerMs * deltaTime}px`;
  
  requestAnimationFrame(animate);
}

Fuites mémoire

Stockez toujours et effacez les IDs d’images d’animation lorsque les composants se démontent :

let animationId;

function startAnimation() {
  function animate() {
    // Animation logic here
    animationId = requestAnimationFrame(animate);
  }
  animationId = requestAnimationFrame(animate);
}

function stopAnimation() {
  if (animationId) {
    cancelAnimationFrame(animationId);
    animationId = null;
  }
}

// Clean up on page unload
window.addEventListener('beforeunload', stopAnimation);

Guide de décision rapide

Cas d’usageMeilleur choixRaison
Animations fluidesrequestAnimationFrameSe synchronise avec le rafraîchissement d’écran
Rendu Canvas/WebGLrequestAnimationFrameÉvite le déchirement et les saccades
Polling d’APIsetTimeoutNon lié aux mises à jour visuelles
Anti-rebond de saisie utilisateursetTimeoutNécessite un contrôle précis du délai
Barres de progression pendant l’animationrequestAnimationFrameExigence de retour visuel
Traitement de données en arrière-plansetTimeoutContinue quand l’onglet est inactif
Boucles de jeurequestAnimationFramePerformance optimale et autonomie de batterie

Conclusion

Le choix entre requestAnimationFrame et setTimeout ne concerne pas laquelle est “meilleure”—il s’agit d’utiliser le bon outil pour la tâche. Pour les mises à jour visuelles et les animations, requestAnimationFrame fournit des performances supérieures grâce à la synchronisation avec le navigateur. Pour les besoins de temporisation généraux et les tâches en arrière-plan, setTimeout offre la flexibilité et la prévisibilité dont vous avez besoin. Comprendre ces différences garantit que vos applications JavaScript offrent des expériences utilisateur fluides tout en gérant efficacement les ressources système.

FAQ

Bien que vous puissiez utiliser setTimeout avec un délai de 16,67ms pour approximer 60fps, cela ne se synchronisera pas avec le cycle de rafraîchissement réel du navigateur. Cela entraîne des images perdues, des saccades et des cycles CPU gaspillés. requestAnimationFrame s'adapte automatiquement à la fréquence de rafraîchissement de l'écran.

Oui, requestAnimationFrame s'adapte à la fréquence de rafraîchissement du moniteur. Sur un écran 120Hz, il se déclenche environ 120 fois par seconde. Utilisez toujours le paramètre timestamp pour calculer le delta time afin d'obtenir une vitesse d'animation cohérente sur différents écrans.

Si votre callback dépasse le budget d'image, le navigateur sautera des images pour maintenir la réactivité. Cela cause des saccades visibles. Considérez l'optimisation de votre code, l'utilisation de web workers pour les calculs lourds, ou la réduction de la complexité de l'animation.

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay