Synchrone, asynchrone et boucle d’événements
JavaScript exécute votre code sur un seul fil d’exécution (single thread). Il ne fait qu’une chose à la fois : la fonction en cours se trouve en haut de la pile d’appels (call stack), et la suivante attend qu’elle se termine.
function saluer(nom) {
return `Bonjour ${nom}`;
}
console.log("début");
console.log(saluer("Ada")); // saluer est empilée, exécutée puis dépilée
console.log("fin");
// Affiche : début, Bonjour Ada, fin (dans l'ordre)
Pourquoi l’asynchrone ?
Attendre une réponse réseau peut prendre une seconde. Si JavaScript bloquait le fil pendant ce temps, la page serait figée : plus de clics, plus d’animation. La solution : déléguer l’attente à l’environnement (navigateur ou Node.js) et donner une fonction à rappeler plus tard, un callback.
console.log("A");
setTimeout(() => {
console.log("B"); // exécuté plus tard, par la boucle d'événements
}, 0);
console.log("C");
// Affiche : A, C, B
Même avec un délai de 0, B s’affiche en dernier. setTimeout ne lance pas la fonction : il demande à l’environnement de la placer dans la file des tâches une fois le délai écoulé.
La boucle d’événements
La boucle d’événements (event loop) répète en permanence ce cycle :
- exécuter le code présent dans la pile d’appels jusqu’à ce qu’elle soit vide ;
- exécuter toutes les microtâches en attente (suites de promesses,
queueMicrotask) ; - prendre une tâche dans la file (callback de
setTimeout, événement de clic…) et recommencer.
setTimeout(() => console.log("tâche"), 0);
Promise.resolve().then(() => console.log("microtâche"));
console.log("synchrone");
// synchrone, microtâche, tâche
Le délai de setTimeout est donc un minimum, pas une garantie : si le fil est occupé par un calcul long, le callback attendra.
Le style callback
Avant les promesses, les API asynchrones prenaient un callback, souvent avec la convention Node.js « erreur en premier » :
function lireConfig(callback) {
setTimeout(() => {
const ok = true;
if (!ok) return callback(new Error("Fichier introuvable"));
callback(null, { theme: "sombre" }); // null = pas d'erreur
}, 10);
}
lireConfig((err, config) => {
if (err) return console.error(err.message);
console.log(config.theme);
});
Ce style fonctionne, mais dès que plusieurs opérations dépendent les unes des autres, les callbacks s’imbriquent : c’est le callback hell. Chaque niveau doit gérer ses propres erreurs, et le code dérive vers la droite. Les promesses, vues dans la leçon suivante, résolvent ce problème en représentant un résultat futur par un objet que l’on peut enchaîner.
À retenir
- Une seule chose à la fois dans la pile d’appels.
- L’attente est déléguée ; la suite revient via la file des tâches ou des microtâches.
- Les microtâches passent avant les tâches.
Exercices
Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.
Prévoir l'ordre d'affichage
La fonction
ordre()doit renvoyer une promesse qui se résout avec le tableau des étiquettes dans l’ordre réel d’exécution. Complétez le code pour enregistrer chaque étiquette dansjournalpuis résoudre la promesse après le minuteur.Observez l’ordre obtenu : synchrone, microtâche, tâche.
function ordre() { const journal = []; return new Promise((resolve) => { setTimeout(() => { journal.push("timeout"); // TODO : résoudre la promesse avec journal }, 0); Promise.resolve().then(() => journal.push("promesse")); journal.push("synchrone"); }); }Convention « erreur en premier »
Écrivez
diviserPlusTard(a, b, callback)qui, après unsetTimeoutde 0 ms, appelle :callback(new Error("Division par zéro"))sib === 0;- sinon
callback(null, a / b).
function diviserPlusTard(a, b, callback) { // TODO }Microtâche ou tâche ?
Que affiche ce code ?
setTimeout(() => console.log('X'), 0); Promise.resolve().then(() => console.log('Y')); console.log('Z');- X, Y, Z
- Z, Y, X
- Z, X, Y
- Y, Z, X
Quiz de fin de leçon
Combien d’instructions JavaScript s’exécutent en même temps dans la pile d’appels d’un même fil ?
- Une seule
- Autant que de cœurs du processeur
- Deux : une synchrone et une asynchrone
Que garantit le délai passé à setTimeout ?
- Une exécution exactement après ce délai
- Un délai minimum avant que le callback puisse s'exécuter
- Une exécution avant toutes les promesses
Quel problème le « callback hell » désigne-t-il ?
- Un callback qui n'est jamais appelé
- L'imbrication profonde de callbacks qui rend le code illisible
- Une erreur de syntaxe dans setTimeout
La suite du cours
- Les promesses : créer, enchaîner, gérer les erreurs18 min
- async/await et try/catch16 min
- Combiner les promesses : all, allSettled, race, any15 min
- fetch : consommer une API JSON20 min