Asynchrone — Promise, async/await et fetch
Passer des callbacks à Promise, chaîner async/await, paralléliser avec Promise.all et utiliser fetch en pratique.
JavaScript est à thread unique — pour ne pas bloquer l'exécution pendant un accès
réseau ou disque, il utilise un modèle asynchrone. Ce bloc reconstruit la chaîne
complète : callback (l'approche historique), Promise (l'abstraction standard), et
async/await (la syntaxe moderne). On conclut avec fetch et les pièges réels du
HTTP dans le navigateur.
⏳ Pourquoi asynchrone ?
Une opération asynchrone est une tâche dont le résultat n'est pas disponible immédiatement — appel réseau, lecture de fichier, timer, requête IndexedDB. Sans mécanisme asynchrone, le thread principal serait bloqué en attendant, gelant toute l'interface.
// ❌ Bloquant (dans un cadre théorique) — le navigateur gèle pendant 2 secondes
function attendreEtCharger() {
const debut = Date.now()
while (Date.now() - debut < 2000) { /* attente active */ }
return chargerDepuisReservation()
}JavaScript résout cela via l'Event Loop : les opérations asynchrones sont déléguées à des APIs du système (ou du navigateur), et la callback est placée dans une file de tâches qui sera traitée quand le thread est libre.
🤝 Callback → Promise
Historiquement, l'asynchrone s'exprimait avec des callbacks :
function lireFichier(chemin, callback) {
// simulation
setTimeout(() => callback(null, "contenu du fichier"), 100)
}
lireFichier("data.txt", (err, contenu) => {
if (err) return console.error(err)
console.log(contenu)
})Le problème : les callbacks s'empilent vite ("callback hell") quand on enchaîne plusieurs opérations dépendantes.
Promise — une valeur future
Une Promise représente une valeur qui sera disponible plus tard. Elle est dans
un de trois états : pending (en attente), fulfilled (résolue), ou
rejected (échouée).
function lireFichier(chemin) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.2) {
resolve("contenu simulé")
} else {
reject(new Error("Fichier introuvable"))
}
}, 100)
})
}
lireFichier("data.txt")
.then(contenu => console.log(contenu))
.catch(err => console.error("Erreur :", err.message))Chaîner des opérations
lireFichier("config.json")
.then(config => {
return lireFichier(`${config.path}/données.json`)
})
.then(donnees => {
return traiter(donnees)
})
.then(resultat => {
console.log("Résultat final :", resultat)
})
.catch(err => {
console.error("Erreur à n'importe quelle étape :", err)
})Chaque .then() retourne une nouvelle Promise — si la callback retourne une valeur,
la Promise est fulfillée avec cette valeur ; si elle retourne une Promise, on
attend sa résolution. Un seul .catch() à la fin capture toutes les erreurs de
la chaîne.
catch = capture toutes les erreurs
Si une étape de la chaîne lève une erreur ou retourne une Promise rejetée,
l'exécution saute directement au .catch() suivant — aucune étape intermédiaire
n'est ignorée. C'est l'erreur .catch() oublié qui cause les "Unhandled Promise
Rejection".
🕊️ async/await — syntaxe moderne
async/await est du sucre syntaxique sur les Promises. Une fonction async retourne
toujours une Promise, et await suspend l'exécution jusqu'à la résolution de
cette Promise.
async function chargerProfil(userId) {
const response = await fetch(`/api/users/${userId}`)
const user = await response.json()
return user
}Gestion d'erreur avec try/catch
async function chargerProfil(userId) {
try {
const response = await fetch(`/api/users/${userId}`)
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
const user = await response.json()
return user
} catch (err) {
console.error("Impossible de charger le profil :", err.message)
throw err // relever si l'appelant doit le gérer
}
}Paralléliser avec Promise.all
Quand plusieurs opérations sont indépendantes, les exécuter en parallèle au lieu d'enchaîner :
async function chargerTout() {
const [users, posts, config] = await Promise.all([
fetch("/api/users").then(r => r.json()),
fetch("/api/posts").then(r => r.json()),
fetch("/api/config").then(r => r.json()),
])
return { users, posts, config }
}Avec Promise.all, si une Promise échoue, tout échoue (la première à rejeter).
Utiliser Promise.allSettled() quand on veut récupérer tous les résultats (succès
ET échecs) :
const results = await Promise.allSettled([
fetch("/a").then(r => r.json()),
fetch("/b").then(r => r.json()),
])
results.forEach(r => {
if (r.status === "fulfilled") console.log("OK:", r.value)
else console.error("Échec:", r.reason)
})all vs allSettled vs race
Promise.all: parallele, échoue à la première Promise rejetée.Promise.allSettled: parallele, retourne tous les résultats (succès/échec).Promise.race: retourne la première Promise qui se termine (succès ou échec).Promise.any(ES2021) : retourne la première Promise fulfillée (ignore les rejets, sauf si toutes échouent).
🌐 fetch en pratique
fetch est l'API native du navigateur pour les requêtes HTTP. Il retourne une
Promise qui se résout avec un objet Response.
GET simple
async function getUsers() {
const response = await fetch("/api/users")
const users = await response.json()
return users
}POST avec un body JSON
async function creerUser(nom, email) {
const response = await fetch("/api/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ nom, email }),
})
if (!response.ok) {
throw new Error(`Création échouée : ${response.status}`)
}
return response.json() // le serveur renvoie l'user créé
}Piège critique : fetch ne rejette pas les erreurs HTTP
Contrairement à Axios ou XMLHttpRequest, fetch ne rejette pas la Promise sur
un 404 ou un 500. Il résout toujours avec un Response dont .ok est false.
const response = await fetch("/api/produit/introuvable")
console.log(response.ok) // false pour un 404
// ❌ Sans vérification — on essaie de parser un corps d'erreur comme du JSON
const data = await response.json() // peut échouer si le serveur renvoie du HTML d'erreur
// ✅ Vérifier response.ok avant de parser
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
const data = await response.json()fetch ne rejette pas les 4xx/5xx
C'est le piège #1 avec fetch. Toujours vérifier response.ok (ou response.status)
avant de faire .json() ou .text(). Une erreur réseau (pas de connexion, DNS
échoué) par contre rejette bien la Promise fetch.
Timeout manuel (fetch n'a pas de timeout natif)
async function fetchAvecTimeout(url, timeoutMs = 5000) {
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), timeoutMs)
try {
const response = await fetch(url, { signal: controller.signal })
return await response.json()
} finally {
clearTimeout(timeout)
}
}🛠️ Exercice
Promise, async/await et fetch - pratique
Maîtriser les Promises, async/await et l'API fetch
Vous allez créer des fonctions asynchrones pour gérer des délais, paralléliser des opérations et effectuer des requêtes HTTP avec vérification des erreurs.
- Créer une Promise qui se résout après un délai donné
- Paralléliser des Promises avec Promise.all
- Utiliser fetch avec vérification du statut HTTP
Partie A — attendre(ms)
Créez une fonction attendre(ms) qui retourne une Promise qui se résout après ms millisecondes. Utilisez setTimeout pour implémenter le délai.
Partie B — attendreTout(msList)
Créez une fonction attendreTout(msList) qui utilise Promise.all pour attendre tous les délais d'un tableau en parallèle et retourne un tableau des résolutions.
Indice pour Promise.all
Promise.all prend un tableau de Promesses et retourne une Promise qui se résout avec un tableau des valeurs. Utilisez .map() sur msList pour transformer chaque délai en Promise via attendre(), puis passez le résultat à Promise.all().
Partie C — fetchWithCheck(url)
Créez une fonction fetchWithCheck(url) qui effectue un fetch(url), vérifie response.ok (lève une Error contenant le status HTTP si non OK), et retourne response.json().
Piège fetch : les erreurs HTTP ne rejettent pas
Contrairement à d'autres bibliothèques, fetch ne rejette pas la Promise sur un statut 404 ou 500. La Promise se résout toujours avec un objet Response dont la propriété ok est false. Vous devez vérifier response.ok manuellement avant d'appeler .json() — c'est l'objet de la Partie C.
Code à valider
🧩 Synthèse du bloc
Promises
Abstraction pour une valeur future. then/catch pour chaîner, un seul catch pour tout le pipeline.
async / await
Sucre syntaxique sur les Promises. async retourne toujours une Promise, await suspend jusqu'à résolution. try/catch pour les erreurs.
Parallélisation
Promise.all pour du parallele (échoue à la 1ère erreur), allSettled pour récupérer tout, race pour la première terminée.
fetch
API native HTTP. Retourne une Response — vérifier response.ok avant .json() (fetch ne rejette pas 4xx/5xx).
Quiz de fin de bloc
Quiz du chapitre
JavaScript avancé
Terminez le quiz du chapitre pour le marquer comme complété.
Itération, collections et Symbol
for…of, Map, Set, WeakMap, WeakSet et l'usage de Symbol pour des clés non énumérables.
Atelier — Manipuler objets, destructuration et objets globaux
Mise en pratique assistée : créez un profil utilisateur, déstructurez-le et comparez les environnements navigateur (window/document) et Node.js (globalThis).