L’asynchrone est le concept JavaScript qui bloque le plus longtemps les développeurs qui apprennent. Callbacks, Promises, async/await, l’event loop — ces notions semblent abstraites jusqu’à ce que vous fassiez un vrai projet avec des appels réseau. Ce guide part de zéro et vous amène à une compréhension réelle, pas mémorisée.
Pourquoi JavaScript a besoin de l’asynchrone
JavaScript est single-threadé : il ne peut faire qu’une chose à la fois. Si un appel réseau prenait 3 secondes à compléter de façon synchrone, toute l’interface se figerait pendant 3 secondes. L’asynchrone permet de « mettre en attente » une opération longue et de continuer à exécuter autre chose pendant ce temps.
L’event loop — sans les schémas compliqués
Le moteur JavaScript a trois zones :
- La call stack : où s’exécute le code synchrone. Une seule chose à la fois.
- La Web API / Node API : où les opérations async (fetch, setTimeout) sont gérées en dehors du thread principal
- La callback queue / microtask queue : où les callbacks async attendent d’être exécutés quand la call stack est vide
L’event loop surveille la call stack. Quand elle est vide, elle prend le prochain callback de la queue et le pousse dans la stack. Les microtasks (Promises) ont priorité sur les macrotasks (setTimeout, setInterval).
Les Promises — comment elles fonctionnent
// Une Promise représente une valeur qui sera disponible dans le futur
const promise = new Promise((resolve, reject) => {
// Opération async...
if (success) resolve(data)
else reject(new Error('Échec'))
})
// Consommer une Promise
promise
.then(data => console.log(data)) // appelé si resolve
.catch(err => console.error(err)) // appelé si reject
.finally(() => console.log('done')) // appelé dans tous les cas
// Plusieurs Promises en parallèle
const [users, products] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/products').then(r => r.json()),
])
async/await — la syntaxe moderne
// async/await est du sucre syntaxique sur les Promises
async function fetchUser(id: number) {
try {
const response = await fetch(`/api/users/${id}`)
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const user = await response.json()
return user
} catch (error) {
console.error('Erreur fetch:', error)
throw error // re-throw pour que l'appelant puisse aussi gérer
}
}
// await ne peut s'utiliser que dans une fonction async
const user = await fetchUser(1) // ✅ dans une fonction async
const user = fetchUser(1) // ❌ Promise non awaited — user = Promise
Les pièges courants
await en boucle
// ❌ Séquentiel — attend chaque fetch avant de démarrer le suivant
for (const id of ids) {
const user = await fetchUser(id) // 3 fetches séquentiels = 3x le temps
}
// ✅ Parallèle — lance tous les fetches en même temps
const users = await Promise.all(ids.map(id => fetchUser(id)))
Promise non gérée
// ❌ L'erreur est silencieuse — unhandled promise rejection
fetchUser(1) // pas de .catch() ni de try/catch
// ✅ Toujours gérer les erreurs
fetchUser(1).catch(err => handleError(err))
// ou
try { await fetchUser(1) } catch (e) { handleError(e) }
Race condition
// ❌ Deux requêtes en vol — la réponse la plus lente peut écraser la plus récente
useEffect(() => {
fetchData(query).then(setData)
}, [query])
// ✅ Annuler la requête précédente avec AbortController
useEffect(() => {
const controller = new AbortController()
fetch(url, { signal: controller.signal })
.then(r => r.json())
.then(setData)
.catch(err => { if (err.name !== 'AbortError') throw err })
return () => controller.abort()
}, [url])
Offres JavaScript
Des postes pour développeurs JavaScript juniors qui maîtrisent les fondamentaux du langage.
À lire aussi : Consommer une API REST — Apprendre JavaScript — ES2025