Cours JavaScript et TypeScript · Module 4: JavaScript asynchrone
Event Loop (modèle d'événements)
Dans cette leçon8
Bienvenue de retour au Parc Jurassique ! Jusqu'à présent, nous nous sommes concentrés sur les bases de la programmation orientée objet - construction de classes, héritage et composition. Cependant, dans des applications réelles comme un système de gestion de parc à dinosaures, nous devons souvent gérer des opérations qui prennent beaucoup de temps - de la récupération de données depuis les capteurs des enclos, à la communication avec les systèmes de sécurité, en passant par la gestion des interactions avec les visiteurs du parc.
Dans ce module, nous allons aborder un aspect clé de JavaScript : sa nature asynchrone et le modèle d'événements sur lequel il repose. Comprendre ce mécanisme est essentiel pour construire des applications réactives et efficaces, capables de gérer de nombreuses opérations simultanément - ce qui est crucial pour la sécurité d'un parc rempli de prédateurs !
JavaScript - un langage mono-thread aux capacités asynchrones
JavaScript est un langage mono-thread (single-threaded). Cela signifie qu'il ne peut exécuter qu'une seule opération à la fois. C'est un peu comme n'avoir qu'un seul employé pour gérer toutes les tâches du parc.
Imagine que cet employé (le thread JavaScript) doive :
- Vérifier l'état des clôtures
- Surveiller les signes vitaux des dinosaures
- Répondre aux messages des visiteurs
- Contrôler les systèmes d'urgence
S'il effectuait ces tâches de façon synchrone (l'une après l'autre), il devrait terminer complètement une tâche avant de passer à la suivante. Ce serait inefficace et potentiellement dangereux !
Heureusement, JavaScript dispose d'un système ingénieux appelé « event loop » (boucle d'événements) qui lui permet de gérer des opérations asynchrones malgré son fonctionnement mono-thread.
Event Loop - le cœur du JavaScript asynchrone
Pour comprendre l'event loop, imaginons le centre de contrôle du Parc Jurassique. Dans ce centre, nous avons :
- Call Stack (pile d'appels) - une liste des tâches en cours d'exécution (les tâches principales de notre employé)
- Callback Queue (file de callbacks) - une liste des tâches en attente d'exécution une fois les opérations asynchrones terminées
- Web APIs (dans les navigateurs) ou C++ APIs (dans Node.js) - des systèmes externes capables d'effectuer des tâches « en arrière-plan »
- Event Loop - un mécanisme qui vérifie en continu si la pile d'appels est vide, et si c'est le cas, déplace les tâches de la file vers la pile
Voyons comment cela fonctionne avec un exemple du Parc Jurassique :
1console.log("1. Démarrage de la ronde matinale du parc.");
2
3setTimeout(() => {
4 console.log("4. Données reçues des capteurs de l'enclos des dinosaures.");
5}, 2000);
6
7fetch('https://jurassic-park-api.com/security-status')
8 .then(response => response.json())
9 .then(data => {
10 console.log("5. État du système de sécurité :", data.status);
11 });
12
13console.log("2. Vérification du planning du personnel d'aujourd'hui.");
14console.log("3. Ronde matinale terminée.");Si nous exécutons ce code, nous verrons dans la console :
11. Démarrage de la ronde matinale du parc.
22. Vérification du planning du personnel d'aujourd'hui.
33. Ronde matinale terminée.
44. Données reçues des capteurs de l'enclos des dinosaures.
55. État du système de sécurité : activeQue s'est-il passé ? Analysons cela étape par étape :
console.log("1. Démarrage de la ronde matinale du parc.")est ajouté à la pile d'appels, exécuté, puis retiré de la pile.setTimeout(...)est ajouté à la pile d'appels. JavaScript reconnaît qu'il s'agit d'un appel asynchrone et le transmet à la Web API, qui démarre le minuteur.setTimeoutest retiré de la pile.fetch(...)est ajouté à la pile d'appels. JavaScript reconnaît qu'il s'agit d'un appel asynchrone et le transmet à la Web API, qui envoie la requête HTTP.fetchest retiré de la pile.console.log("2. Vérification du planning du personnel d'aujourd'hui.")est ajouté à la pile, exécuté, puis retiré.console.log("3. Ronde matinale terminée.")est ajouté à la pile, exécuté, puis retiré.La pile d'appels est maintenant vide, mais deux opérations asynchrones sont en cours :
- Le minuteur de
setTimeout - La requête HTTP de
fetch
- Le minuteur de
Au bout de 2 secondes, le minuteur expire et le callback de
setTimeoutest ajouté à la file de tâches.L'event loop voit que la pile d'appels est vide, il déplace donc le callback de la file vers la pile.
console.log("4. Données reçues des capteurs de l'enclos des dinosaures.")est exécuté puis retiré de la pile.Pendant ce temps, la requête HTTP se termine, les données sont récupérées et le callback
.then()est ajouté à la file.L'event loop déplace à nouveau le callback de la file vers la pile.
Les données sont traitées et
console.log("5. État du système de sécurité :", data.status)est exécuté.
Event Loop - analyse détaillée
Pour mieux comprendre l'event loop, dessinons ses principaux composants :
1┌─────────────┐ ┌───────────┐ ┌───────────────────┐
2│ │ │ │ │ │
3│ Call Stack │ │ Web APIs │ │ Callback Queue │
4│ │ │ │ │ │
5└─────────────┘ └───────────┘ └───────────────────┘
6 ↑ │ │
7 │ ↓ │
8 │ ┌────────┐ │
9 └────────────│ Event │←─────────────┘
10 │ Loop │
11 └────────┘Dans la représentation graphique :
- Call Stack - une pile LIFO (Last In, First Out) qui stocke les appels de fonctions en cours d'exécution
- Web APIs - interfaces du navigateur/environnement pour les opérations asynchrones (setTimeout, fetch, Event Listeners)
- Callback Queue - une file FIFO (First In, First Out) qui stocke les callbacks prêts à être exécutés
- Event Loop - vérifie en continu si la pile d'appels est vide, et si c'est le cas, déplace les fonctions de la file vers la pile
Le plus étonnant, c'est que tout cela tourne sur un seul thread ! JavaScript n'exécute pas ces tâches en parallèle, mais bascule entre elles de manière à donner l'impression que tout se passe simultanément.
Microtasks et Macrotasks
Dans les implémentations JavaScript récentes (et dans la spécification), on distingue deux types de files :
- Microtask Queue - pour les tâches de plus haute priorité, comme les callbacks de Promise
- Macrotask Queue / Task Queue - pour les tâches de plus basse priorité, comme setTimeout, setInterval, I/O
Lorsque la pile est vide, toutes les tâches de la file de microtasks sont exécutées en premier, puis celles de la file de macrotasks.
1console.log("1. Début du nourrissage matinal des dinosaures.");
2
3// Macrotask (setTimeout)
4setTimeout(() => {
5 console.log("4. Vérification de l'état de santé des dinosaures.");
6}, 0);
7
8// Microtask (Promise)
9Promise.resolve().then(() => {
10 console.log("3. Lecture des données du système de sécurité.");
11});
12
13console.log("2. Préparation de la nourriture pour les prédateurs.");Résultat :
11. Début du nourrissage matinal des dinosaures.
22. Préparation de la nourriture pour les prédateurs.
33. Lecture des données du système de sécurité.
44. Vérification de l'état de santé des dinosaures.Remarque que, bien que les deux tâches asynchrones (setTimeout avec 0ms et Promise.resolve()) soient prêtes à s'exécuter presque immédiatement, la Promise (microtask) s'exécute avant le setTimeout (macrotask).
Bloquer l'Event Loop
Comme JavaScript est mono-thread, les opérations longues peuvent « bloquer » l'event loop et empêcher les autres tâches de s'exécuter. C'est comme si l'employé du centre de contrôle restait bloqué devant un seul écran sans pouvoir vérifier les autres systèmes.
1console.log("Démarrage de la simulation de croissance des dinosaures...");
2
3// Fonction qui bloque l'exécution pendant 5 secondes
4function heavyComputation() {
5 const startTime = Date.now();
6 while (Date.now() - startTime < 5000) {
7 // Calculs intensifs...
8 }
9}
10
11heavyComputation();
12console.log("Simulation terminée !"); // Après 5 secondes
13
14// Ce code ne s'exécutera pas avant 5 secondes, même s'il est asynchrone !
15setTimeout(() => {
16 console.log("Vérification des systèmes de sécurité...");
17}, 1000);Dans cet exemple, la fonction heavyComputation() bloque le thread JavaScript pendant 5 secondes. Pendant ce temps, aucune autre tâche ne peut être exécutée, y compris les tâches asynchrones. C'est comme si l'employé du parc restait bloqué sur une tâche sans pouvoir répondre aux alarmes ou à d'autres signaux importants.
Dans un vrai Parc Jurassique, un tel blocage pourrait être catastrophique - un raptor pourrait s'échapper et le système ne l'enregistrerait pas car il est occupé à autre chose !
C'est pourquoi, en JavaScript, on essaie de :
- Éviter les opérations synchrones longues
- Découper les calculs complexes en morceaux plus petits
- Utiliser les Web Workers pour les opérations gourmandes en CPU
Ordre d'exécution des tâches - exemple pratique
Analysons un exemple plus complexe qui illustre l'ordre d'exécution de différents types de tâches au Parc Jurassique :
1// Simulation du système de contrôle du Parc Jurassique
2console.log("1. Démarrage du système de contrôle du Parc Jurassique...");
3
4// Macrotask (setTimeout)
5setTimeout(() => {
6 console.log("6. Vérification des clôtures électriques...");
7
8 // Microtask imbriquée dans une macrotask
9 Promise.resolve().then(() => {
10 console.log("7. Vérification du statut des prédateurs...");
11 });
12}, 0);
13
14// Microtask (Promise)
15Promise.resolve().then(() => {
16 console.log("3. Initialisation des caméras de sécurité...");
17
18 // Microtask imbriquée
19 Promise.resolve().then(() => {
20 console.log("4. Calibration des capteurs de mouvement...");
21 });
22});
23
24// Une autre microtask
25Promise.resolve().then(() => {
26 console.log("5. Lancement des protocoles d'urgence...");
27});
28
29// Code synchrone
30console.log("2. Vérification de l'état du système...");Résultat :
11. Démarrage du système de contrôle du Parc Jurassique...
22. Vérification de l'état du système...
33. Initialisation des caméras de sécurité...
44. Calibration des capteurs de mouvement...
55. Lancement des protocoles d'urgence...
66. Vérification des clôtures électriques...
77. Vérification du statut des prédateurs...Cet exemple montre que :
- Le code synchrone s'exécute en premier (1, 2)
- Puis toutes les tâches de la file de microtasks s'exécutent (3, 4, 5)
- Ce n'est qu'ensuite que les tâches de la file de macrotasks s'exécutent (6)
- Les microtasks créées pendant une macrotask (7) s'exécutent immédiatement après la fin de la macrotask en cours
Bonus : Var, Let, Const - quand utiliser lequel ?
Lorsqu'on travaille avec du code asynchrone, il est important de déclarer correctement les variables. La manière de déclarer affecte leur comportement dans les blocs et les fonctions, ce qui a un impact direct sur le code asynchrone (par exemple, les variables dans des boucles avec setTimeout). Tout comme le parc dispose de différents types de clôtures selon les espèces de dinosaures (électrique pour le T-Rex, standard pour le Triceratops), JavaScript propose différentes façons de déclarer les variables : var, let et const.
Const - toujours le choix par défaut
const signifie une constante - une valeur qui ne peut pas être réassignée. Au Parc Jurassique, on utilise const pour les éléments qui ne doivent pas changer, comme les paramètres de sécurité ou la configuration du système :
1// Configuration du système - ne devrait pas changer
2const MAX_DINOSAURS_PER_ENCLOSURE = 5;
3const FENCE_MINIMUM_VOLTAGE = 10000; // volts
4const EMERGENCY_CONTACT = "+1-555-JURASSIC";
5
6// Tenter de modifier une const provoque une erreur
7MAX_DINOSAURS_PER_ENCLOSURE = 10; // TypeError: Assignment to constant variableQuand utiliser const :
- Pour les valeurs qui ne changeront pas
- Pour les objets et les tableaux (même si leur contenu change)
- Comme choix par défaut - en cas de doute, utilise
const
Important : const ne signifie pas que le contenu d'un objet ou d'un tableau est immuable !
1// C'est correct - on modifie le contenu, pas l'assignation
2const dinosaurs = [];
3dinosaurs.push({ name: "Rexy", species: "T-Rex" }); // OK !
4dinosaurs.push({ name: "Blue", species: "Velociraptor" }); // OK !
5
6const parkConfig = {
7 openTime: "09:00",
8 closeTime: "18:00"
9};
10parkConfig.closeTime = "20:00"; // OK - on modifie une propriété
11
12// Ceci provoque une erreur - tentative de réassignation
13dinosaurs = []; // TypeError !
14parkConfig = {}; // TypeError !Let - quand la valeur change
let s'utilise lorsque la valeur d'une variable change pendant l'exécution du programme. Au Parc Jurassique, on utilise let pour les éléments dynamiques comme les compteurs, les accumulateurs ou les variables de boucle :
1// Compteur de visiteurs - change tout au long de la journée
2let visitorCount = 0;
3
4function addVisitors(count) {
5 visitorCount += count;
6 console.log(`Nombre actuel de visiteurs : ${visitorCount}`);
7}
8
9addVisitors(50); // 50
10addVisitors(30); // 80
11addVisitors(20); // 100Quand utiliser let :
- Dans les boucles (for, while)
- Pour les compteurs et les accumulateurs
- Quand la valeur sera réassignée
1// Boucles - cas d'usage classique de let
2for (let i = 0; i < 5; i++) {
3 console.log(`Vérification du dinosaure n°${i}`);
4}
5
6// Accumulateurs
7let totalFeedingCost = 0;
8
9const dinosaurs = [
10 { name: "Rexy", dailyFoodCost: 500 },
11 { name: "Blue", dailyFoodCost: 150 },
12 { name: "Trixie", dailyFoodCost: 200 }
13];
14
15for (const dino of dinosaurs) {
16 totalFeedingCost += dino.dailyFoodCost;
17}
18
19console.log(`Coût total du nourrissage : ${totalFeedingCost} $`);Var - presque jamais (code hérité)
var est l'ancienne façon de déclarer des variables en JavaScript. Son comportement est problématique et ne devrait pas être utilisé dans du code moderne. Le seul endroit où tu verras var, c'est dans de l'ancien code (legacy) écrit avant 2015 (avant ES6).
Problèmes avec var :
- Portée de fonction au lieu de portée de bloc -
var« s'échappe » des blocs
1// Problème : var ignore les blocs
2function checkDinosaur(isDangerous) {
3 if (isDangerous) {
4 var alert = "DANGER !"; // var est accessible en dehors du bloc if !
5 }
6
7 console.log(alert); // "DANGER !" - fuit hors du bloc if !
8 // Avec let/const, on obtiendrait une ReferenceError
9}
10
11checkDinosaur(true);
12
13// Comparons avec let :
14function checkDinosaurProper(isDangerous) {
15 if (isDangerous) {
16 let alert = "DANGER !";
17 }
18
19 console.log(alert); // ReferenceError: alert is not defined
20}- Hoisting avec undefined -
varest « hissé » (hoisted) en haut de la fonction
1// Comportement étrange de var
2function feedDinosaurs() {
3 console.log(food); // undefined (et non ReferenceError !)
4 var food = "viande";
5 console.log(food); // "viande"
6}
7
8// C'est interprété comme :
9function feedDinosaurs() {
10 var food; // hoisting - déclaration en haut
11 console.log(food); // undefined
12 food = "viande"; // assignation
13 console.log(food); // "viande"
14}- Peut être redéclarée - on peut écraser des variables par accident
1var dinosaurName = "Rexy";
2// On réutilise accidentellement le même nom
3var dinosaurName = "Blue"; // Aucune erreur ! La valeur précédente est écrasée
4
5console.log(dinosaurName); // "Blue" - Rexy a disparu !
6
7// Avec let/const :
8let dinoName = "Rexy";
9let dinoName = "Blue"; // SyntaxError: Identifier 'dinoName' has already been declaredQuand utiliser var :
- Jamais dans du nouveau code
- Uniquement en éditant du très vieux code dont tu dois préserver la compatibilité
Exemple pratique - revue de code
Imagine que tu reçois le code d'un collègue à relire. Voici comment améliorer l'usage des variables :
Mauvais code (avant) :
1// Mauvais - tout en var
2var parkName = "Jurassic Park"; // Ne change pas - devrait être const
3var isOpen = true; // Change - devrait être let
4var maxCapacity = 10000; // Ne change pas - devrait être const
5
6function processVisitors() {
7 var totalVisitors = 0; // Accumulateur - devrait être let
8
9 for (var i = 0; i < 100; i++) { // Itérateur - devrait être let
10 totalVisitors++;
11 }
12
13 console.log(i); // 100 - var fuit ! Problème !
14 return totalVisitors;
15}Bon code (après) :
1// Bon - const par défaut
2const parkName = "Jurassic Park";
3const maxCapacity = 10000;
4
5// let uniquement quand la valeur change
6let isOpen = true;
7
8function processVisitors() {
9 // let pour l'accumulateur
10 let totalVisitors = 0;
11
12 // let dans la boucle - portée de bloc
13 for (let i = 0; i < 100; i++) {
14 totalVisitors++;
15 }
16
17 // console.log(i); // ReferenceError - i est limité à la boucle
18 return totalVisitors;
19}
20
21// On peut modifier isOpen
22isOpen = false;
23isOpen = true;
24
25// Mais on ne peut pas modifier une const
26// parkName = "Dino World"; // TypeError !Autre exemple - système de gestion des dinosaures
1class DinosaurManagementSystem {
2 constructor() {
3 // const pour les propriétés dont la référence ne change pas
4 this.dinosaurs = []; // le contenu du tableau peut changer, mais pas la référence
5 this.config = { // le contenu de l'objet peut changer, mais pas la référence
6 maxDinosaurs: 50,
7 securityLevel: "high"
8 };
9 }
10
11 addDinosaur(name, species, dangerLevel) {
12 // const pour les paramètres de fonction et les valeurs locales qui ne changent pas
13 const newDinosaur = {
14 id: Date.now(),
15 name,
16 species,
17 dangerLevel,
18 status: "active"
19 };
20
21 this.dinosaurs.push(newDinosaur);
22 return newDinosaur;
23 }
24
25 getDangerousCount() {
26 // let pour l'accumulateur
27 let count = 0;
28
29 // const dans for...of quand on ne modifie pas la variable de boucle
30 for (const dino of this.dinosaurs) {
31 if (dino.dangerLevel > 7) {
32 count++; // on modifie count, d'où let
33 }
34 }
35
36 return count;
37 }
38
39 calculateDailyFoodCost() {
40 // let pour les valeurs qui changent
41 let totalCost = 0;
42
43 // let dans une boucle for classique
44 for (let i = 0; i < this.dinosaurs.length; i++) {
45 const dino = this.dinosaurs[i]; // const - on ne réassigne pas la référence
46
47 // const pour les valeurs calculées
48 const dailyCost = this.calculateDinosaurFoodCost(dino);
49 totalCost += dailyCost;
50 }
51
52 return totalCost;
53 }
54
55 calculateDinosaurFoodCost(dinosaur) {
56 // const pour toutes les valeurs qui ne changent pas
57 const baseCost = 50;
58 const costMultiplier = dinosaur.dangerLevel > 5 ? 2 : 1;
59 const dailyCost = baseCost * costMultiplier;
60
61 return dailyCost;
62 }
63}
64
65// Exemple d'utilisation
66const parkSystem = new DinosaurManagementSystem(); // const - la référence ne change pas
67
68parkSystem.addDinosaur("Rexy", "T-Rex", 9);
69parkSystem.addDinosaur("Blue", "Velociraptor", 8);
70parkSystem.addDinosaur("Trixie", "Triceratops", 3);
71
72// let pour les valeurs qui vont changer
73let dangerousCount = parkSystem.getDangerousCount();
74console.log(`Dinosaures dangereux : ${dangerousCount}`);
75
76// const pour les valeurs à usage unique
77const foodCost = parkSystem.calculateDailyFoodCost();
78console.log(`Coût quotidien de nourriture : ${foodCost} $`);
79
80// On peut ajouter d'autres dinosaures
81parkSystem.addDinosaur("Delta", "Velociraptor", 8);
82
83// Et recalculer
84dangerousCount = parkSystem.getDangerousCount(); // let autorise la réassignation
85console.log(`Dinosaures dangereux : ${dangerousCount}`);Règle de pouce
- Commence toujours par
const- si le compilateur/linter se plaint que tu essaies de changer la valeur, passe àlet - N'utilise
letque lorsque c'est nécessaire - boucles, compteurs, accumulateurs - N'utilise jamais
var- sauf pour éditer du vieux code
1// Bonne pratique
2const maxDinosaurs = 50;
3let currentDinosaurs = 0;
4
5for (let i = 0; i < maxDinosaurs; i++) {
6 const dinosaur = createDinosaur();
7 currentDinosaurs++;
8}
9
10// Mauvaise pratique
11var maxDinosaurs = 50;
12var currentDinosaurs = 0;
13
14for (var i = 0; i < maxDinosaurs; i++) {
15 var dinosaur = createDinosaur();
16 currentDinosaurs++;
17}Pourquoi est-ce important ?
Tout comme des clôtures adaptées au Parc Jurassique empêchent les dinosaures de s'échapper, un bon usage de const et let prévient les bugs dans le code :
- Lisibilité - on voit immédiatement quelles valeurs changent (
let) et lesquelles non (const) - Sécurité -
constprotège contre les modifications accidentelles - Portée de bloc - les variables ne « fuient » pas hors des blocs
- Débogage plus simple - moins de surprises dues au hoisting
Souviens-toi : en JavaScript moderne, var est comme les dinosaures - une relique intéressante du passé, mais que tu ne veux pas dans ton code de production !
Résumé
Le modèle d'événements de JavaScript (event loop) est la clé pour comprendre comment ce langage mono-thread peut gérer de nombreuses opérations asynchrones - indispensable dans des applications comme le système de gestion du Parc Jurassique.
Retiens les principes clés :
- JavaScript est mono-thread mais asynchrone
- L'event loop permet de gérer de nombreuses tâches sans bloquer l'interface utilisateur
- Les tâches s'exécutent dans un ordre défini : synchrone → microtasks → macrotasks
- Évite de bloquer l'event loop avec de longues opérations synchrones
Dans les prochaines leçons, nous verrons comment utiliser ces connaissances pour écrire du code asynchrone efficace à l'aide des callbacks, des Promises et de async/await.
Code de cette leçon : index.js
1// Event Loop and asynchronicity in Jurassic Park
2console.log("Centre de Contrôle du Parc Jurassique - Event Loop Demo");
3console.log("Simulation of asynchronous park management system\n");
4
5// ===========================================
6// 1. Les bases de l'Event Loop - Call Stack
7// ===========================================
8console.log("=== 1. CALL STACK - Execution order ===");
9
10function checkFence() {
11 console.log("1. Vérification de la clôture du T-Rex...");
12 return "Clôture sécurisée";
13}
14
15function checkDinosaur() {
16 console.log("2. Vérification de l'état du dinosaure...");
17 const result = checkFence(); // Synchronous call
18 console.log("3. " + result);
19 return "Dinosaure sécurisé";
20}
21
22function reportBezpieczenstwa() {
23 console.log("0. Starting security report");
24 const status = checkDinosaur();
25 console.log("4. Report: " + status);
26}
27
28// Exécution synchrone - call stack
29reportBezpieczenstwa();
30
31// ===========================================
32// 2. setTimeout - Callback Queue
33// ===========================================
34console.log("\n=== 2. SETTIMEOUT - Asynchronous operations ===");
35
36console.log("A. Je démarre la surveillance du parc...");
37
38setTimeout(() => {
39 console.log("C. Alarme ! Mouvement détecté dans l'enclos du T-Rex !");
40}, 2000);
41
42setTimeout(() => {
43 console.log("D. Système automatique : Sécurités supplémentaires activées");
44}, 1000);
45
46console.log("B. Continuing other tasks...");
47
48// ===========================================
49// 3. Park sensor simulation
50// ===========================================
51console.log("\n=== 3. PARK SENSOR SIMULATION ===");
52
53class DinosaurSensor {
54 constructor(name, dinosaur) {
55 this.name = name;
56 this.dinosaur = dinosaur;
57 this.isActive = true;
58 }
59
60 checkStatus(callback) {
61 console.log(`Sensor ${this.name}: Checking ${this.dinosaur}...`);
62
63 // Simulating sensor delay
64 const delay = Math.random() * 3000 + 1000; // 1-4 secondes
65
66 setTimeout(() => {
67 const status = Math.random() > 0.8 ? 'ALARM' : 'SÛR';
68 const report = {
69 czujnik: this.name,
70 dinosaur: this.dinosaur,
71 status: status,
72 czas: new Date().toLocaleTimeString()
73 };
74 callback(report);
75 }, delay);
76 }
77}
78
79// Callback hell - problem with nested callbacks
80function monitorPark() {
81 console.log("\nJe démarre le monitoring du parc...");
82
83 const czujnikTRex = new DinosaurSensor("TR-001", "T-Rex");
84 const czujnikVelociraptor = new DinosaurSensor("VR-002", "Velociraptor");
85 const czujnikTriceratops = new DinosaurSensor("TC-003", "Triceratops");
86
87 // Callback hell - each sensor one by one
88 czujnikTRex.checkStatus((reportTRex) => {
89 console.log("T-Rex report:", reportTRex);
90
91 czujnikVelociraptor.checkStatus((reportVelo) => {
92 console.log("Velociraptor report:", reportVelo);
93
94 czujnikTriceratops.checkStatus((reportTrice) => {
95 console.log("Triceratops report:", reportTrice);
96
97 // Analysis of all reports
98 const reporty = [reportTRex, reportVelo, reportTrice];
99 const alarms = reporty.filter(r => r.status === 'ALARM');
100
101 if (alarms.length > 0) {
102 console.log("WARNING! Alarms detected:", alarms.length);
103 alarms.forEach(alarm => {
104 console.log(` ${alarm.dinosaur} - ${alarm.status}`);
105 });
106 } else {
107 console.log("All dinosaurs safe");
108 }
109 });
110 });
111 });
112}
113
114// Lance le monitoring après 3 secondes
115setTimeout(monitorPark, 3000);
116
117// ===========================================
118// 4. Promises - solving Callback Hell
119// ===========================================
120console.log("\n=== 4. PROMISES - Better asynchronous management ===");
121
122class ModernSensor {
123 constructor(name, dinosaur) {
124 this.name = name;
125 this.dinosaur = dinosaur;
126 }
127
128 checkStatus() {
129 return new Promise((resolve, reject) => {
130 console.log(`Promise: Checking ${this.dinosaur}...`);
131
132 const delay = Math.random() * 2000 + 500;
133
134 setTimeout(() => {
135 const success = Math.random() > 0.1; // 90% de chances de succès
136
137 if (success) {
138 const status = Math.random() > 0.7 ? 'ALARM' : 'SÛR';
139 resolve({
140 czujnik: this.name,
141 dinosaur: this.dinosaur,
142 status: status,
143 czas: new Date().toLocaleTimeString()
144 });
145 } else {
146 reject(new Error(`Sensor ${this.name} ne répond pas`));
147 }
148 }, delay);
149 });
150 }
151}
152
153function monitorParkPromises() {
154 console.log("\nPromise-based monitoring...");
155
156 const modernTRex = new ModernSensor("MTR-001", "Modern T-Rex");
157 const modernVelo = new ModernSensor("MVR-002", "Modern Velociraptor");
158 const modernTrice = new ModernSensor("MTC-003", "Modern Triceratops");
159
160 // Promise chain
161 modernTRex.checkStatus()
162 .then(report => {
163 console.log("Promise T-Rex:", report);
164 return modernVelo.checkStatus();
165 })
166 .then(report => {
167 console.log("Promise Velociraptor:", report);
168 return modernTrice.checkStatus();
169 })
170 .then(report => {
171 console.log("Promise Triceratops:", report);
172 console.log("Tous les capteurs Promise vérifiés !");
173 })
174 .catch(error => {
175 console.log("Error in Promise chain:", error.message);
176 });
177
178 // Promise.all - all at once
179 console.log("\nPromise.all - parallel checking...");
180
181 Promise.all([
182 modernTRex.checkStatus(),
183 modernVelo.checkStatus(),
184 modernTrice.checkStatus()
185 ])
186 .then(reporty => {
187 console.log("Tous les rapports Promise.all prêts :");
188 reporty.forEach(report => {
189 console.log(` ${report.dinosaur}: ${report.status}`);
190 });
191
192 const alarms = reporty.filter(r => r.status === 'ALARM');
193 if (alarms.length > 0) {
194 console.log(`Promise.all: ${alarms.length} alarms!`);
195 } else {
196 console.log("Promise.all: Park safe!");
197 }
198 })
199 .catch(error => {
200 console.log("Promise.all error:", error.message);
201 });
202}
203
204// Lance le monitoring Promise après 8 secondes
205setTimeout(monitorParkPromises, 8000);
206
207// ===========================================
208// 5. Async/Await - cleanest approach
209// ===========================================
210console.log("\n=== 5. ASYNC/AWAIT - JavaScript asynchrone moderne ===");
211
212class AsyncSensor {
213 constructor(name, dinosaur) {
214 this.name = name;
215 this.dinosaur = dinosaur;
216 }
217
218 async checkStatus() {
219 console.log(`Async: Checking ${this.dinosaur}...`);
220
221 const delay = Math.random() * 1500 + 500;
222
223 // Async/await avec Promise
224 return new Promise((resolve, reject) => {
225 setTimeout(() => {
226 const success = Math.random() > 0.05; // 95% de succès
227
228 if (success) {
229 const status = Math.random() > 0.6 ? 'ALARM' : 'SÛR';
230 resolve({
231 czujnik: this.name,
232 dinosaur: this.dinosaur,
233 status: status,
234 czas: new Date().toLocaleTimeString(),
235 delay: delay.toFixed(0) + 'ms'
236 });
237 } else {
238 reject(new Error(`Erreur du capteur Async ${this.name}`));
239 }
240 }, delay);
241 });
242 }
243}
244
245// Async function
246async function monitorParkAsync() {
247 console.log("\nAsync/Await monitoring started...");
248
249 try {
250 const asyncTRex = new AsyncSensor("ATR-001", "Async T-Rex");
251 const asyncVelo = new AsyncSensor("AVR-002", "Async Velociraptor");
252 const asyncTrice = new AsyncSensor("ATC-003", "Async Triceratops");
253
254 // Séquentiellement (l'un après l'autre)
255 console.log("Sequential checking:");
256 const reportTRex = await asyncTRex.checkStatus();
257 console.log(" Async T-Rex:", reportTRex);
258
259 const reportVelo = await asyncVelo.checkStatus();
260 console.log(" Async Velociraptor:", reportVelo);
261
262 const reportTrice = await asyncTrice.checkStatus();
263 console.log(" Async Triceratops:", reportTrice);
264
265 // Parallel with Promise.all
266 console.log("\nParallel checking async/await:");
267 const [reportT2, reportV2, reportTr2] = await Promise.all([
268 asyncTRex.checkStatus(),
269 asyncVelo.checkStatus(),
270 asyncTrice.checkStatus()
271 ]);
272
273 const allReports = [reportT2, reportV2, reportTr2];
274 console.log("Parallel results:");
275 allReports.forEach(report => {
276 console.log(` ${report.dinosaur}: ${report.status} (${report.delay})`);
277 });
278
279 // Security analysis
280 const dangerous = allReports.filter(r => r.status === 'ALARM');
281 if (dangerous.length > 0) {
282 console.log(`\nASYNC ALERT: ${dangerous.length} dinosaurs need attention!`);
283 dangerous.forEach(dino => {
284 console.log(` ${dino.dinosaur} - intervention required`);
285 });
286 } else {
287 console.log("\nASYNC SUCCESS: All dinosaurs under control!");
288 }
289
290 } catch (error) {
291 console.log("Async/await error:", error.message);
292 }
293}
294
295// Lance le monitoring async après 15 secondes
296setTimeout(monitorParkAsync, 15000);
297
298// ===========================================
299// 6. L'Event Loop en pratique - Microtasks vs Macrotasks
300// ===========================================
301console.log("\n=== 6. EVENT LOOP - Execution order ===");
302
303function demonstracjaEventLoop() {
304 console.log("\nEvent Loop demo - execution order:");
305
306 console.log("1. Code synchrone");
307
308 setTimeout(() => {
309 console.log("4. setTimeout (Macro task)");
310 }, 0);
311
312 Promise.resolve().then(() => {
313 console.log("3. Promise.then (Micro task)");
314 });
315
316 console.log("2. Code synchrone - fin");
317
318 // Order: 1, 2, 3, 4
319 // Les microtasks (Promise) ont une priorité plus élevée que les macrotasks (setTimeout)
320}
321
322demonstracjaEventLoop();
323
324// ===========================================
325// 7. Practical example - Park alert system
326// ===========================================
327console.log("\n=== 7. PARK ALERT SYSTEM ===");
328
329class ParkAlertSystem {
330 constructor() {
331 this.alerts = [];
332 this.isMonitoring = false;
333 }
334
335 async startMonitoring() {
336 this.isMonitoring = true;
337 console.log("Alert system started monitoring...");
338
339 while (this.isMonitoring) {
340 await this.checkSystems();
341 await this.czekaj(2000); // Vérifie toutes les 2 secondes
342 }
343 }
344
345 stopMonitoring() {
346 this.isMonitoring = false;
347 console.log("⏹Monitoring stopped");
348 }
349
350 async checkSystems() {
351 const systemy = ['fence', 'alimentation', 'communication', 'security'];
352
353 const obietnice = systemy.map(system => this.checkSystem(system));
354
355 try {
356 const results = await Promise.allSettled(obietnice);
357
358 results.forEach((result, index) => {
359 const systemName = systemy[index];
360 if (result.status === 'fulfilled') {
361 if (result.value.status === 'ALARM') {
362 this.addAlert(systemName, result.value.message);
363 }
364 } else {
365 this.addAlert(systemName, `System error: ${result.reason}`);
366 }
367 });
368
369 if (this.alerts.length > 0) {
370 this.displayAlertsAndClear();
371 }
372
373 } catch (error) {
374 console.log("Critical monitoring error:", error);
375 }
376 }
377
378 async checkSystem(system) {
379 const delay = Math.random() * 1000 + 200;
380
381 return new Promise((resolve, reject) => {
382 setTimeout(() => {
383 const isWorking = Math.random() > 0.2; // 80% chance of working
384
385 if (isWorking) {
386 const hasAlert = Math.random() > 0.8; // 20% de chances d'alerte
387 resolve({
388 system,
389 status: hasAlert ? 'ALARM' : 'OK',
390 message: hasAlert ? `Problem with system ${system}` : `System ${system} is working correctly`
391 });
392 } else {
393 reject(`System ${system} ne répond pas`);
394 }
395 }, delay);
396 });
397 }
398
399 addAlert(system, message) {
400 this.alerts.push({
401 system,
402 message,
403 time: new Date().toLocaleTimeString()
404 });
405 }
406
407 displayAlertsAndClear() {
408 console.log(`\nPARK ALERT (${this.alerts.length} problems):`);
409 this.alerts.forEach(alert => {
410 console.log(` [${alert.time}] ${alert.system}: ${alert.message}`);
411 });
412 this.alerts = []; // Clear after display
413 }
414
415 async czekaj(ms) {
416 return new Promise(resolve => setTimeout(resolve, ms));
417 }
418}
419
420// Alert system test
421const alertSystem = new ParkAlertSystem();
422
423// Lance le système après 20 secondes, pour 10 secondes
424setTimeout(async () => {
425 await alertSystem.startMonitoring();
426
427 // Arrête après 10 secondes
428 setTimeout(() => {
429 alertSystem.stopMonitoring();
430 }, 10000);
431}, 20000);
432
433// ===========================================
434// 8. Résumé de l'Event Loop
435// ===========================================
436setTimeout(() => {
437 console.log("\n" + "=".repeat(50));
438 console.log("RÉSUMÉ EVENT LOOP :");
439 console.log("1. JavaScript is single-threaded");
440 console.log("2. Event Loop permet l'asynchronicité");
441 console.log("3. Callbacks → Promises → Async/Await");
442 console.log("4. Les microtasks ont une priorité plus élevée que les macrotasks");
443 console.log("5. Async/Await est la syntaxe la plus propre");
444 console.log("=".repeat(50));
445}, 35000);Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Qu'est-ce que l'Event Loop en JavaScript ?