Formation publique · Jour 5 / 6

Asynchrone & Network

Sections

  1. 01 Promesses (45 min)
  2. 02 async / await (45 min)
  3. 03 fetch GET + panneau Network (60 min)
  4. 04 États UI, erreurs & Network approfondi (50 min)
  5. 05 localStorage / sessionStorage (45 min)
  6. Extra 1 axios – aperçu (Extra)
  7. Extra 2 Mini-chat (Extra)
  8. Récap Lab libre

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 attente
  • fulfilled – 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

  • fetch est-il la nouvelle façon de faire ? C’est l’API native moderne recommandée pour les nouveaux développements dans le navigateur. XMLHttpRequest existe 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 » ? fetch convient 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 :

  1. URL incorrecte → 404 (changer l’URL dans le code)
  2. Mode Offline dans Network → erreur réseau
  3. Throttling « Slow 3G » → voir le loading longtemps
DevTools ouverts sur Network en mode 3G pendant le chargement des projets.

À 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 :

Menu de throttling du panneau Network avec les options No throttling, Fast 4G, Slow 4G, 3G et Offline.
  • 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 fetch du navigateur
    • XHR – requêtes lancées avec XMLHttpRequest, l’API historique du navigateur
  • Onglet Timing – DNS, connexion, attente, téléchargement
Menu Copy d’une requête Network avec les options Copy URL, cURL, PowerShell et fetch.

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.

Panneau Network avec le menu contextuel Replay XHR sur une requête axios.

À 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.php avec { "user": "…", "message": "…" } → ajoute un message
  • POST /day-5/api/chat.php avec { "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 !

Récap — Lab libre