setInterval() en JavaScript : répéter un traitement à intervalle régulier

setInterval() rappelle une fonction toutes les X millisecondes : le décalage qui s'installe, le risque de chevauchement, et l'alternative en setTimeout récursif.
3 min de lecture
Believemy logo

Un compte à rebours, une horloge, un rafraîchissement de données toutes les trente secondes : ces besoins ont l'air identiques, et pourtant l'un d'eux tolère mal la solution évidente.

setInterval est simple à écrire et facile à mal utiliser. Comprendre ce qu'il garantit, et surtout ce qu'il ne garantit pas, évite la plupart des ennuis.


Définition

setInterval(callback, delai) demande à l'environnement de replanifier la même fonction toutes les delai millisecondes, indéfiniment, jusqu'à l'appel de clearInterval avec l'identifiant renvoyé.

JAVASCRIPT
let restant = 3;

const id = setInterval(() => {
  console.log(restant);
  restant--;
  if (restant === 0) {
    clearInterval(id);
    console.log("terminé");
  }
}, 1000);

// 3, 2, 1, terminé

La condition d'arrêt vit à l'intérieur du callback : sans elle, l'intervalle tourne tant que la page est ouverte, ou tant que le processus vit sous Node.js.


Le décalage qui s'installe

L'intervalle décompte à partir de la planification, pas de la fin du callback. Si le traitement dure plus longtemps que le délai, le rythme demandé devient impossible à tenir et l'écart se creuse à chaque tour.

JAVASCRIPT
const t0 = Date.now();
let tour = 0;

const id = setInterval(() => {
  tour++;
  const debut = Date.now();
  while (Date.now() - debut < 30) {}   // 30 ms de travail dans un intervalle de 10
  console.log("tour", tour, "à", Date.now() - t0, "ms");
  if (tour === 3) clearInterval(id);
}, 10);

Les tours ne tombent jamais à 10, 20 et 30 millisecondes : chacun démarre après la fin du précédent. Une horloge fondée sur le nombre de tours dérive donc, là où une horloge qui relit Date.now reste juste.


La version qui ne se chevauche pas

Pour un travail de durée variable, un appel réseau par exemple, on replanifie soi-même avec setTimeout() après chaque exécution. Le délai devient une pause entre deux tours, ce qui interdit tout empilement.

JAVASCRIPT
async function rafraichirEnBoucle(pas) {
  while (true) {
    await lireDonnees();                                  // durée inconnue
    await new Promise((r) => setTimeout(r, pas));         // pause après coup
  }
}

// ou, sans async :
function boucle(pas) {
  setTimeout(() => { faireLeTravail(); boucle(pas); }, pas);
}
Bon à savoir

Un intervalle oublié est une fuite classique. Dans une interface à composants, tout setInterval créé à l'affichage doit être annulé au retrait, sans quoi il continue de tourner sur un écran qui n'existe plus et déclenche des mises à jour dans le vide.


Questions fréquentes

Question

Les exécutions peuvent-elles se chevaucher ?

Pas sur le fil : la boucle d'événements n'exécute qu'une chose à la fois, donc deux tours ne tournent jamais ensemble. En revanche la Promise lancée par un tour, une requête par exemple, peut très bien être encore en cours quand le tour suivant démarre. C'est là que les données se mélangent.


Question

Que se passe-t-il dans un onglet en arrière-plan ?

Les navigateurs espacent fortement les intervalles d'un onglet caché, souvent à une exécution par seconde au mieux. Un compte à rebours fondé sur le nombre de tours affichera donc une valeur fausse au retour : recalculez toujours depuis un horodatage de départ.


Question

Faut-il préférer setTimeout récursif ?

Dès que la durée du traitement est incertaine, oui. setInterval reste parfaitement adapté aux traitements courts et prévisibles, comme le rafraîchissement d'un affichage. Pour tout ce qui appelle le réseau, la version récursive évite les tours qui se marchent dessus.

Termes connexes

Découvrez notre glossaire JavaScript

Tous les mots de JavaScript expliqués simplement : mots-clés, objets natifs, méthodes, erreurs et concepts. Définitions claires et exemples qui tournent, pour apprendre et pour se dépanner.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.