Apprenez en lisant dans l'ordre

Minuteurs et dates — setTimeout / Date / Intl

setTimeout, l'arrêt de setInterval, les mois de Date comptés à partir de 0, l'affichage avec Intl.DateTimeFormat et l'écart en jours.

Fermer une notification au bout de 3 secondes, ou décompter les secondes restantes une fois par seconde : du code qui s'exécute simplement de haut en bas n'en est pas capable. Et tant qu'une date reste une chaîne, tu ne peux ni afficher le jour de la semaine d'une date de publication, ni compter les jours qui restent avant une échéance.

Cet article couvre setTimeout et setInterval, qui créent des minuteurs, ainsi que Date et Intl.DateTimeFormat, qui servent à manipuler les dates et les heures.

Programmer un appel pour plus tard — setTimeout et clearTimeout

Sur un site de partage de photos, tu affiches la notification « Photo publiée » et tu veux la fermer au bout de 3 secondes. Du code qui s'exécute de haut en bas n'a aucun moyen de dire « attends 3 secondes, puis passe à la suite ». Et si tu fais tourner une boucle for à vide pour gagner du temps, la page cesse de réagir aux clics et de se mettre à jour tant que la boucle tourne.

setTimeout(fonction, millisecondes) se contente de programmer l'appel : l'exécution passe aussitôt à la ligne suivante. La fonction renvoie un identifiant de minuteur, le même genre d'identifiant que celui du timeout de fetch, et le passer à clearTimeout annule l'appel.

// Renvoie une Promise fulfilled au bout de ms millisecondes (le delay de l'article sur Promise, sans l'argument value)
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// Programme la fermeture de la notification dans 100 ms et passe tout de suite à la ligne suivante (plutôt 3000 ms sur une vraie page)
setTimeout(() => console.log("Notification fermée"), 100);   // Notification fermée (2e)
console.log("Photo publiée");                                // Photo publiée (1re)

// L'utilisateur a quitté la page : on annule l'affichage des recommandations. Il n'a jamais lieu, même après 100 ms
const recommendTimer = setTimeout(() => console.log("Photos recommandées affichées"), 100);
clearTimeout(recommendTimer);

// On attend 200 ms avant de continuer
await delay(200);
console.log("200 ms plus tard");                             // 200 ms plus tard (3e)
L'ordre d'écriture n'est pas l'ordre d'affichage
0 msPhoto publiéeAffiché en 1ersetTimeout seuln'affiche rien0 msRecommandationsannuléesJamais appelée,même à 100 ms100 msNotificationferméeAffiché en 2e,pendant l'await200 ms200 ms plus tardLigne après awaitaffichée en 3e
La ligne setTimeout n'affiche rien, et l'appel annulé n'a jamais lieu. La fonction programmée s'exécute à 100 ms, pendant l'attente de await.

Une fonction programmée peut s'exécuter alors que la dernière ligne de ton code est déjà terminée. Pour voir ce qu'elle affiche dans la console des exercices, termine ton code par une ligne qui attend, comme await delay(…), ainsi que tu l'as fait dans l'article sur Promise.

Dans une application de messagerie, tu retardes l'envoi pour qu'on puisse encore l'annuler. delay est déjà déclaré.

① Programme l'affichage de « E-mail 1 envoyé » dans 150 ms, attends 50 ms, puis annule-le.

② Affiche « Envoi de l'e-mail 1 annulé ».

③ Programme l'affichage de « E-mail 2 envoyé » dans 100 ms, attends 150 ms, puis annule-le.

④ Affiche « Impossible d'annuler l'e-mail 2 ».

(Si tout s'exécute correctement, une explication apparaîtra.)

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat

Arrêter une répétition — setInterval et clearInterval

Tu veux afficher le nombre de secondes avant la fin d'une vente flash, en le diminuant chaque seconde. setTimeout n'appelle la fonction programmée qu'une seule fois : pour répéter, il faudrait que la fonction se reprogramme elle-même à chaque appel, et la condition d'arrêt se retrouverait noyée dans ce même code.

setInterval (une fonction qui appelle la même fonction encore et encore, toutes les N millisecondes) continue de l'appeler jusqu'à ce que tu passes à clearInterval l'identifiant de minuteur qu'elle a renvoyé.

Pour n'exécuter la ligne suivante qu'une fois la répétition terminée, enveloppe le tout dans une Promise et appelle resolve juste après clearInterval, dans le même if.

// Diminue de 1 le nombre de secondes restantes toutes les 100 ms (toutes les 1000 ms sur une vraie page)
function startCountdown(seconds) {
  return new Promise((resolve) => {
    let remaining = seconds;                  // Déclarée hors de la fonction appelée, pour garder sa valeur d'un appel à l'autre
    const timerId = setInterval(() => {
      remaining -= 1;
      console.log(`Temps restant : ${remaining} s`);   // Temps restant : 2 s → 1 s → 0 s
      if (remaining === 0) {
        clearInterval(timerId);               // On arrête à l'appel qui atteint 0 (timerId est déjà affecté à ce moment-là)
        resolve();                            // On prévient la ligne await que c'est fini
      }
    }, 100);
  });
}

await startCountdown(3);
console.log("La vente flash est terminée");   // La vente flash est terminée
resolve fait avancer await
Appelant — la ligne await startCountdown(3)
  • Ne passe pas à la ligne suivante tant que la Promise renvoyée n'est pas fulfilled
  • La vente flash est terminée s'affiche après resolve
Dans startCountdown(3) — s'exécute une fois
  • let remaining = seconds — créée une seule fois ici, avec la valeur 3
  • const timerId = setInterval(...) — l'identifiant à passer pour arrêter
Fonction appelée toutes les 100 ms — 3 fois
  • remaining -= 1 — diminue la variable externe : 2 → 1 → 0
  • À l'appel qui atteint 0 : clearInterval(timerId) et resolve()
La variable externe remaining n'est créée qu'une fois et diminue à chaque appel. Le resolve de l'appel qui atteint 0 fait avancer le await de l'appelant.

Si tu déplaces let remaining = seconds dans la fonction appelée, la variable repart de 3 à chaque appel et n'atteint jamais 0 : ni clearInterval ni resolve ne sont appelés. Au bout de 15 secondes sans avancer, la console des exercices indique que l'exécution a expiré.

Une répétition jamais arrêtée se mélange aux exécutions suivantes

Dans cet article, la console des exercices utilise la même page pour toutes les exécutions. Si une exécution se termine sans appeler clearInterval, sa fonction continue d'être appelée jusqu'à ce que tu recharges la page. Ce qu'elle affiche se mélange alors aux résultats des exercices lancés ensuite qui attendent avec await, et même un code correct est jugé faux. Si ça arrive, recharge la page de l'article.

Tu relèves le stock d'un entrepôt toutes les 100 ms et tu t'arrêtes au bout de 3 relevés. Le squelette de watchStock, le tableau des quantités readings et delay sont déjà déclarés.

① Toutes les 100 ms, lis la valeur suivante de readings et affiche-la sous la forme « Relevé 1 : 12 en stock ».

② Arrête la répétition au 3e relevé et fais passer la Promise à fulfilled.

③ Attends la fin, puis attends encore 200 ms pour vérifier qu'aucun 4e relevé ne s'affiche.

④ Affiche « Vérification du stock terminée ».

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat

Transformer une date en texte à afficher — getMonth et Intl

Dans une liste de vidéos, tu veux afficher la date de publication sous la forme « mer. 2 septembre 2026 ». Si tu extrais avec slice le mois d'une chaîne envoyée par l'API, comme "2026-09-02T09:00:00", tu obtiens la chaîne "09". Et le jour de la semaine n'y figure nulle part : impossible de le trouver à partir de la chaîne seule.

Date (un objet intégré qui représente une date et une heure) lit "2026-09-02T09:00:00" comme 9 h dans le fuseau horaire de l'appareil (l'heure locale d'une région, définie par son décalage avec l'heure de référence UTC), et ses méthodes qui commencent par get renvoient le mois, le jour de la semaine, etc., sous forme de nombres.

Intl.DateTimeFormat (une classe qui met en forme dates et heures selon les conventions d'une langue) se crée en lui passant une locale, par exemple "fr-FR" (français de France).

const publishedAt = new Date("2026-09-02T09:00:00");

// Les méthodes en get renvoient des nombres. Janvier vaut 0 pour les mois, dimanche vaut 0 pour les jours de la semaine
console.log(publishedAt.getMonth(), publishedAt.getDate(), publishedAt.getDay());   // 8 2 3

// On crée un format en passant la langue et la forme de chaque élément à afficher
const dateFormat = new Intl.DateTimeFormat("fr-FR", {
  year: "numeric",     // L'année en chiffres
  month: "long",       // Le mois en toutes lettres, comme "septembre"
  day: "numeric",      // Le jour en chiffres
  weekday: "short",    // Le jour de la semaine en abrégé
});
console.log(dateFormat.format(publishedAt));   // mer. 2 septembre 2026
console.log(new Intl.DateTimeFormat("fr-FR", { hour: "numeric", minute: "2-digit" }).format(publishedAt));   // 9:00

// Une chaîne sans heure est lue comme minuit UTC
const dateOnly = new Date("2026-09-02");
console.log(dateOnly.getDate());   // 2 au Japon, 1 à New York (la veille à 20 h)
Sans l'heure, on obtient minuit UTC
"2026-09-02T09:00:00"Lue comme 9 hheure localeÀ New York aussi :2 sept., 9 hgetDate() vaut 2même jour qu'àTokyo"2026-09-02"sans l'heureLue commeminuit UTCÀ New York :1er sept., 20 hgetDate() vaut 1c'est la veille
Avec l'heure, la chaîne désigne le 2 septembre à 9 h, quel que soit le fuseau horaire. Sans l'heure, elle est lue en UTC et peut tomber la veille selon le décalage.

Dans les fuseaux en avance sur UTC, comme au Japon, tu obtiens quand même 2 : le problème passe donc facilement inaperçu. Quand l'API envoie une date sans heure, ajoute "T00:00:00" avant de la passer à new Date, pour qu'elle soit lue comme minuit en heure locale. Le tableau ci-dessous résume ce que renvoient les méthodes en get pour publishedAt.

MéthodepublishedAtPour l'affichage
getFullYear()2026Tel quel
getMonth()8Janvier = 0 : +1 → 9
getDate()2Commence à 1 : tel quel
getDay()3Dimanche = 0 : 3 = mer.
getHours()9Format 24 h, de 0 à 23

Sur la page de réservation d'un cabinet dentaire, tu mets en forme la date et l'heure d'un rendez-vous. reservedAt, qui les contient, est déjà déclaré.

① Avec les méthodes en get, affiche la date du rendez-vous sous la forme « 5/10 ».

② Crée un format en français qui affiche le jour de la semaine, le jour et le mois sous la forme « lun. 5 octobre ».

③ Affiche la date du rendez-vous avec le format de ②.

④ Crée un format qui affiche l'heure et les minutes sous la forme « 14:30 », et affiche l'heure du rendez-vous.

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat

Compter les jours avant une échéance — soustraire des objets Date

Sur le site d'une bibliothèque, tu veux afficher le nombre de jours qui restent avant la date de retour d'un livre. Si tu soustrais seulement les numéros de jour, un livre emprunté le 2 septembre et à rendre le 3 octobre donne 3 − 2 = 1 jour : les jours du mois de septembre qui séparent les deux dates ne sont pas comptés.

Un objet Date contient, sous forme de nombre, les millisecondes écoulées depuis le 1er janvier 1970 à minuit (UTC). Soustraire un objet Date d'un autre renvoie cet écart : en le divisant par le nombre de millisecondes d'une journée, 24 * 60 * 60 * 1000, tu obtiens un nombre de jours.

new Date(année, mois, jour) crée ce jour-là à minuit, et là aussi, les mois se comptent à partir de 0.

// Millisecondes dans une journée (24 heures × 60 minutes × 60 secondes × 1000)
const DAY_MS = 24 * 60 * 60 * 1000;

// Date d'emprunt et date de retour. Même quand on les crée avec des nombres, les mois se comptent à partir de 0
const borrowedAt = new Date(2026, 8, 2);     // 2 septembre 2026
const dueDate = new Date(2026, 9, 3);        // 3 octobre 2026

// Soustraire un objet Date d'un autre donne l'écart en millisecondes
const diffMs = dueDate - borrowedAt;
console.log(Math.round(diffMs / DAY_MS));    // 31

// Passer 31, un jour qui n'existe pas en septembre, ne lève pas d'erreur : on bascule sur le mois suivant
const overflowDate = new Date(2026, 8, 31);
console.log(overflowDate.getMonth() + 1, overflowDate.getDate());   // 10 1
Trois façons d'écrire le 30 septembre
Jours entrenew Date(2026,8,2)et l'échéancenew Date(2026, 8, 30)new Date(2026, 9, 30)new Date(2026, 8, 31)Crée le30 septembre30 octobremois 9 = octobre1er octobrepas de 31 sept.28 jours58 jours30 jours de trop29 jours1 jour de trop
Les trois lignes sont censées fixer la date de retour au 30 septembre. Passer 9 comme mois ou 31 comme jour ne lève pas d'erreur : seul le nombre de jours est faux.

Le code arrondit avec Math.round à cause de l'heure d'été (le fait d'avancer les horloges d'une heure pendant l'été). Là où elle s'applique, certaines journées durent 23 ou 25 heures, et la division ne tombe pas toujours juste : tu peux obtenir par exemple 30.958… au lieu d'un entier. Au Japon, où il n'y a pas d'heure d'été, la division tombe toujours juste, et on oublie facilement d'arrondir.

Sur le site de réservation d'un camping, tu calcules le nombre de nuits d'un séjour. Les squelettes de toDate et countNights, ainsi que DAY_MS, sont déjà déclarés.

① Dans toDate, crée et renvoie un objet Date à partir d'une année, d'un mois compté à partir de 1 et d'un jour.

② Dans countNights, renvoie le nombre de nuits à partir de l'écart entre deux objets Date.

③ Affiche le nombre de nuits du 28 septembre au 2 octobre 2026 sous la forme « 4 nuits ».

④ Affiche sous la même forme le nombre de nuits du 29 décembre 2026 au 3 janvier de l'année suivante.

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat
QUIZ

Vérification des connaissances

Répondez à chaque question une par une.

Question 1Que se passe-t-il si tu déclares let count = 0; dans la fonction passée à setInterval et que tu essaies de l'arrêter quand count atteint 3 ?

Question 2Que renvoie new Date("2026-09-02T09:00:00").getMonth() ?

Question 3Que renvoie getDate() pour un objet Date créé avec new Date(2026, 8, 31) ?