1 Promesses (45 min)
Jusqu’ici le code s’exécutait de haut en bas, ligne après ligne. Maintenant le résultat arrive plus tard.
1.1 Qu’est-ce qu’une Promise
Une Promise est un objet qui représente une valeur qui n’est pas encore disponible.
Erreur classique : utiliser le résultat avant que la Promise soit résolue.
const domain = "https://training.dercetech.com";
const apiRoot = domain + "/trainings/javascript-vanilla-devtools/day-5/api/";
const randomEntry = fetch(apiRoot + "random-insta-page-I-like.php");
console.log(randomEntry); // Promise { <pending> }
console.log(randomEntry.url, randomEntry.category); // undefined undefined
fetch renvoie immédiatement une Promise. L’objet avec l’URL et la catégorie arrivera plus tard.
Elle a trois états possibles :
pending– en attentefulfilled– réussie (valeur disponible)rejected– échouée (erreur disponible)
Une Promise ne change d’état qu’une seule fois. Ensuite elle est figée.
1.2 Créer une Promise
const p = new Promise((resolve, reject) => {
// travail asynchrone ici
// puis :
resolve("valeur finale");
// ou :
// reject(new Error("quelque chose a échoué"));
});
resolve et reject sont des fonctions fournies par le constructeur.
On appelle l’une ou l’autre, jamais les deux.
const tirage = new Promise((resolve, reject) => {
const nombre = Math.random();
if (nombre < 0.5) {
reject(new Error(`échec : ${nombre}`));
} else {
resolve(`succès : ${nombre}`);
}
});
1.3 Consommer une Promise – then / catch / finally
p
.then((value) => {
console.log("succès :", value);
})
.catch((error) => {
console.error("échec :", error);
})
.finally(() => {
console.log("terminé (succès ou échec)");
});
.then reçoit la valeur de resolve.
.catch reçoit la raison de reject.
.finally s’exécute dans les deux cas. Utile pour le nettoyage.
1.4 Chaînage
Chaque .then retourne une nouvelle Promise. On peut enchaîner.
Promise.resolve(2)
.then((n) => n * 3)
.then((n) => n + 1)
.then((n) => console.log(n)); // 7
1.5 Observer dans les DevTools
Dans la Console, une Promise non résolue s’affiche Promise { <pending> }.
Quand elle se résout, la valeur apparaît.
Dans Sources, posez un breakpoint à l’intérieur d’un .then ou d’un .catch. Vous verrez la pile d’appels au moment de la résolution.
À essayer
- Ouvrez le dossier www/day-5/s1_promises.
- Travaillez uniquement dans scripts.js.
- Suivez les commentaires dans l’ordre.
- Après chaque bloc : sauvegardez + rafraîchissez.
- Ouvrez les DevTools (F12) → Console + Sources. Inspectez les objets Promise et posez des breakpoints dans les callbacks.
Checkpoint
Vous savez créer une Promise, la résoudre ou la rejeter, la consommer avec then / catch / finally, et observer son état dans la Console et dans Sources.
2 async / await (45 min)
Les Promesses sont puissantes. Leur syntaxe en chaîne devient vite difficile à lire. async/await est du sucre syntaxique par-dessus les Promesses.
2.1 async function
Une fonction déclarée avec async retourne toujours une Promise.
async function exemple() {
return 42;
}
exemple().then((v) => console.log(v)); // 42
Même si vous retournez une valeur simple, elle est automatiquement enveloppée dans une Promise résolue.
2.2 await
await ne peut être utilisé qu’à l’intérieur d’une fonction async.
Il met en pause l’exécution de cette fonction jusqu’à ce que la Promise soit résolue (ou rejetée).
async function demo() {
const value = await Promise.resolve("prêt");
console.log(value); // "prêt"
}
Le reste du code après await s’exécute seulement quand la valeur est disponible.
2.3 Gestion d’erreurs avec try / catch
Avec async/await on utilise le try/catch classique.
async function charge() {
try {
const data = await quelqueChoseQuiPeutEchouer();
console.log(data);
} catch (error) {
console.error("échec :", error.message);
} finally {
console.log("terminé");
}
}
Plus besoin de .catch() séparé. Le flux ressemble à du code synchrone.
2.4 Plusieurs await à la suite
Les await s’exécutent l’un après l’autre (séquentiel).
async function sequential() {
const a = await getA();
const b = await getB(); // attend que getA soit fini
return a + b;
}
Si les deux opérations sont indépendantes, on peut les lancer en parallèle avec Promise.all (aperçu uniquement, pas d’exercice approfondi aujourd’hui).
2.5 Observer dans Sources
Posez un breakpoint sur une ligne await.
Quand l’exécution s’arrête, regardez la pile d’appels : la fonction async est en pause, en attente de la résolution.
C’est l’un des meilleurs moyens de comprendre réellement le mécanisme.
À essayer
- Ouvrez le dossier www/day-5/s2_async-await.
- Travaillez uniquement dans scripts.js.
- Suivez les commentaires dans l’ordre.
- Après chaque bloc : sauvegardez + rafraîchissez.
- Ouvrez Sources, posez des breakpoints sur les lignes await et dans les catch. Observez la pile.
Checkpoint
Vous savez écrire une fonction async, utiliser await, gérer les erreurs avec try/catch, et inspecter l’exécution asynchrone dans le panneau Sources.
3 fetch GET + panneau Network (60 min)
Jusqu’ici les Promesses étaient artificielles (setTimeout). Maintenant on parle au réseau.
3.1 fetch – la forme de base
const domain = "https://training.dercetech.com";
const apiRoot = domain + "/trainings/javascript-vanilla-devtools/day-5/api/";
const response = await fetch(apiRoot + "projects.php");
console.log(response);
fetch retourne une Promise qui se résout en un objet Response.
L’objet Response n’est pas encore les données. C’est l’enveloppe HTTP.
Questions légitimes
fetchest-il la nouvelle façon de faire ? C’est l’API native moderne recommandée pour les nouveaux développements dans le navigateur.XMLHttpRequestexiste toujours.- Pourquoi ne pas écrire une requête XHR à la main ? C’est possible, mais l’API est plus verbeuse et repose sur des événements.
- Existe-t-il une version « pro » ?
fetchconvient au travail professionnel. axios ajoute des raccourcis et des conventions. Nous le verrons dans l’Extra 1.
const xhr = new XMLHttpRequest();
xhr.open("GET", API_URL);
xhr.addEventListener("load", () => {
if (xhr.status >= 200 && xhr.status < 300) {
const data = JSON.parse(xhr.responseText);
console.log(data);
}
});
xhr.addEventListener("error", () => {
console.error("Erreur réseau");
});
xhr.send();
Même résultat. Plus de plomberie.
3.2 Lire le statut
console.log(response.ok); // true si 200–299
console.log(response.status); // 200, 404, 500…
console.log(response.statusText);
Toujours vérifier response.ok avant de lire le corps.
3.3 Lire le corps JSON
const data = await response.json();
console.log(data);
.json() retourne aussi une Promise. D’où le deuxième await.
3.4 Afficher une liste simple
On reçoit un tableau d’objets. On crée des éléments et on les insère.
const list = document.querySelector("#project-list");
data.forEach((project) => {
const li = document.createElement("li");
const a = document.createElement("a");
a.href = project.url;
a.textContent = project.title;
a.target = "_blank";
li.append(a);
list.append(li);
});
3.5 Panneau Network – premier contact obligatoire
Ouvrez les DevTools → onglet Network avant de lancer le fetch.
Cochez « Preserve log ».
Vous devez voir :
- la requête apparaître dans la waterfall
- le code de statut (200)
- l’onglet Headers (Request + Response)
- l’onglet Preview / Response (le JSON)
- le timing
C’est aussi important que le code JavaScript lui-même.
3.6 Indicateur de chargement minimal
Avant le fetch, affichez un texte « Chargement… ».
Après le fetch (succès ou échec), retirez-le ou remplacez-le.
Juste assez pour que l’étudiant voie que le réseau prend du temps.
À essayer
- Ouvrez le dossier www/day-5/s3_fetch-get.
- Travaillez uniquement dans scripts.js.
- Suivez les commentaires dans l’ordre.
- Ouvrez Network avant de cliquer sur le bouton.
- Inspectez chaque requête : statut, headers, corps, timing.
Checkpoint
Vous savez lancer un fetch GET, lire le statut et le JSON, afficher une liste simple, et vous inspectez systématiquement la requête dans le panneau Network.
4 États UI, erreurs & Network approfondi (50 min)
Un fetch réussi n’est qu’un des trois cas possibles. L’interface doit toujours refléter l’état réel.
4.1 Les trois états à gérer
- loading – la requête est en cours
- success – les données sont arrivées
- error – quelque chose a échoué (réseau ou HTTP)
Ces trois états doivent être visibles dans la page, pas seulement dans la Console.
4.2 Afficher l’état dans le DOM
Dans cet exemple, statusEl désigne l’élément #status sélectionné dans le DOM.
function setStatus(state, message) {
statusEl.dataset.state = state; // "loading" | "success" | "error"
statusEl.textContent = message;
}
On utilise un data-state pour pouvoir styler facilement (couleur, icône…).
Le message reste explicite pour l’étudiant.
4.3 Forcer les erreurs
Provoquez et observez au moins trois situations :
- URL incorrecte → 404 (changer l’URL dans le code)
- Mode Offline dans Network → erreur réseau
- Throttling « Slow 3G » → voir le loading longtemps
À chaque fois, le panneau Network et l’interface doivent raconter la même histoire.
4.4 Network – outils du quotidien
Dans l’onglet Network, maîtriser maintenant :
- Throttling – No throttling / Fast 3G / Slow 3G / Offline
- Disable cache – case à cocher (très utile en développement)
- Copy – clic droit sur une requête → Copy → Copy as cURL (cmd ou bash), Copy as PowerShell ou Copy as fetch (voir ci-dessous)
- Filtre par type (Fetch/XHR) et par texte
- Fetch – requêtes lancées avec l’API
fetchdu navigateur - XHR – requêtes lancées avec
XMLHttpRequest, l’API historique du navigateur
- Fetch – requêtes lancées avec l’API
- Onglet Timing – DNS, connexion, attente, téléchargement
Ces outils ne sont pas optionnels. Ils font partie du métier.
Pour approfondir ces sujets, y compris le profiling et le débogage, suivez notre formation « Advanced DevTools ».
4.5 Pattern propre
async function loadProjects() {
// 1. Montrer immédiatement que la requête démarre
setStatus("loading", "Chargement…");
listEl.innerHTML = "";
try {
// 2. Attendre la réponse HTTP
const response = await fetch(API_URL);
// 3. fetch ne rejette pas automatiquement les erreurs 404 / 500
if (!response.ok) {
throw new Error("HTTP " + response.status);
}
// 4. Lire le JSON, puis mettre à jour l’interface
const data = await response.json();
renderList(data);
setStatus("success", data.length + " projets chargés");
} catch (error) {
// 5. Les erreurs réseau et HTTP arrivent ici
console.error(error);
setStatus("error", "Échec : " + error.message);
}
}
Un seul endroit pour le loading, un seul endroit pour le succès, un seul endroit pour l’erreur.
À essayer
- Ouvrez le dossier www/day-5/s4_fetch-states.
- Travaillez uniquement dans scripts.js.
- Suivez les commentaires dans l’ordre.
- Ouvrez Network avant chaque essai.
- Testez systématiquement : cas normal, 404, Offline, Slow 3G.
- Utilisez Copy as cURL, Copy as PowerShell ou Copy as fetch, puis observez le Timing.
Checkpoint
Vous gérez explicitement les trois états UI (loading / success / error), vous forcez et diagnostiquez les erreurs dans Network (throttling, offline, copie, timing), et l’interface raconte toujours la vérité.
5 localStorage / sessionStorage (45 min)
Le réseau est utile. Parfois on a juste besoin de se souvenir de quelque chose entre deux rechargements.
5.1 Deux espaces, une API
localStorage survit à la fermeture du navigateur.
sessionStorage meurt quand l’onglet est fermé.
L’API est identique :
localStorage.setItem("clé", "valeur");
const v = localStorage.getItem("clé"); // string ou null
localStorage.removeItem("clé");
localStorage.clear();
Remplacez localStorage par sessionStorage pour l’autre comportement.
5.2 Seulement des chaînes
Tout ce qui entre dans le storage doit être une string.
// Objet → string
localStorage.setItem("prefs", JSON.stringify({ theme: "dark", filter: "all" }));
// string → objet
const prefs = JSON.parse(localStorage.getItem("prefs") || "{}");
Oublier JSON.stringify / JSON.parse est l’erreur la plus fréquente.
5.3 Cas d’usage simple et réaliste
On garde une préférence utilisateur (filtre, thème, dernier choix…).
Au chargement de la page on restaure la préférence et on l’applique.
Quand l’utilisateur change la préférence, on l’écrit immédiatement.
5.4 Inspection dans les DevTools
Onglet Application (Chrome) ou Storage (Firefox).
Local Storage / Session Storage → domaine → on voit les paires clé/valeur en direct.
On peut les modifier ou les supprimer à la main. Très utile pour tester.
5.5 Limites à connaître
- Environ 5 Mo par origine (varie selon le navigateur)
- Synchrone – ne pas y mettre de gros volumes
- Visible par tout script de la même origine
Pour la plupart des préférences UI, c’est largement suffisant.
À essayer
- Ouvrez le dossier www/day-5/s5_storage.
- Travaillez uniquement dans scripts.js.
- Suivez les commentaires dans l’ordre.
- Ouvrez l’onglet Application → Local Storage et observez les valeurs apparaître / disparaître.
- Rechargez la page et vérifiez que l’état est restauré.
Checkpoint
Vous savez lire et écrire dans localStorage et sessionStorage, sérialiser avec JSON, restaurer un état au chargement, et inspecter le contenu dans l’onglet Application.
x1 axios – aperçu (Extra)
fetch est natif et suffisant. axios est une bibliothèque très répandue qui s’appuie sur les mêmes mécanismes du navigateur.
fetch est-il suffisant en production ? Oui. Il est natif, stable et adapté au travail professionnel.
axios devient intéressant quand une équipe veut standardiser les appels HTTP et réduire le code répétitif :
- transformation automatique du JSON
- erreurs HTTP 4xx / 5xx envoyées directement dans le
catch - configuration commune pour les headers, l’authentification et les délais d’attente
- intercepteurs pour traiter les requêtes et les réponses au même endroit
axios s’adresse surtout aux applications qui multiplient les appels HTTP et veulent une convention partagée. Pour quelques requêtes simples, fetch suffit largement.
Cet extra montre uniquement la forme de base, pour que vous reconnaissiez le code quand vous le rencontrerez.
x1.1 Chargement via CDN
Pas de bundler aujourd’hui. On charge axios avec une balise script :
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
Après ce script, l’objet global axios est disponible.
x1.2 GET simple
const response = await axios.get(API_URL);
console.log(response.data); // les données sont déjà dans .data
Différence importante avec fetch :
- axios parse le JSON automatiquement → on lit
response.data - axios considère les statuts 4xx / 5xx comme des erreurs → elles arrivent dans le
catch
Avec fetch – il faut transformer manuellement un statut HTTP en erreur :
try {
const response = await fetch(API_URL);
if (!response.ok) {
throw new Error("HTTP " + response.status);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error.message);
}
Avec axios – un statut 4xx ou 5xx arrive directement dans le catch :
try {
const response = await axios.get(API_URL);
console.log(response.data);
} catch (error) {
console.error(error.message);
}
x1.3 Même logique d’états UI
On réutilise exactement le même pattern loading / success / error vu en section 04.
Seul le transport change.
x1.4 Network panel
Dans Network, une requête axios apparaît comme n’importe quelle autre requête Fetch/XHR.
Les outils modernes comme axios créent une requête XHR dont les paramètres sont conservés dans Network.
Un clic droit permet alors de choisir Replay XHR pour rejouer la requête. Utile si les données retournées peuvent changer, ou pour rejouer une requête POST, PUT ou DELETE.
À essayer
- Ouvrez le dossier www/day-5/x1_axios.
- Travaillez uniquement dans scripts.js.
- Suivez les commentaires.
- Comparez mentalement avec la version fetch de la section 04.
- Observez la requête dans Network.
Checkpoint
Vous savez charger axios via CDN, faire un GET, lire response.data, et vous avez vu que les erreurs HTTP arrivent directement dans le catch.
x2 Mini-chat (Extra)
Un endpoint un peu plus vivant. On lit des messages, on en envoie, on peut tout effacer.
L’API est volontairement barebones. L’objectif est de pratiquer GET + POST + inspection Network, pas de construire un produit.
x2.1 Les trois opérations
GET /day-5/api/chat.php→ retourne un tableau JSON des messages (max 30)POST /day-5/api/chat.phpavec{ "user": "…", "message": "…" }→ ajoute un messagePOST /day-5/api/chat.phpavec{ "action": "clear" }→ vide le journal
x2.2 Forme des messages
[
{
"user": "alice",
"message": "je déploie le vendredi à 17 h",
"ts": "2026-08-14T17:00:03+02:00"
}
]
x2.3 Côté client
On garde le même pattern d’états UI (loading / success / error).
Deux actions distinctes : charger la liste, envoyer un message.
Après un envoi réussi, on recharge la liste pour voir le nouveau message.
Un polling recharge aussi la liste toutes les secondes avec setTimeout.
async function pollMessages() {
await loadMessages(false);
setTimeout(pollMessages, 1000);
}
pollMessages();
Le paramètre false évite de réafficher « Chargement… » à chaque rafraîchissement automatique.
x2.4 Network
Observez bien la différence entre la requête GET et la requête POST (method, payload, status).
Utilisez Replay et le panneau Timing comme d’habitude.
À essayer
- Ouvrez le dossier www/day-5/x2_chat.
- Travaillez uniquement dans scripts.js.
- Suivez les commentaires dans l’ordre.
- Ouvrez Network avant chaque action.
- Testez l’envoi, le rechargement, et le clear.
- Le chat est partagé et ne conserve aucune traçabilité. Comme dans les années 90. Déchaînez-vous.
- Observez le GET automatique toutes les secondes.
Checkpoint
Vous savez faire un GET et un POST vers la même API, envoyer un corps JSON, gérer les trois états UI, relancer le chargement avec setTimeout, et vous inspectez systématiquement les requêtes dans Network.
La vraie question avant de terminer la journée
Est-ce bien raisonnable, Alice, de déployer un vendredi à 17 h ?
Quoi qu’il en soit, bon week-end à toute l’équipe. Rendez-vous lundi !