first comit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/.idea/
|
||||
10
.idea/.gitignore
generated
vendored
Normal file
10
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Ignored default folder with query files
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
161
README.md
Normal file
161
README.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# TD - Démonstration JavaScript Asynchrone : la cuisine
|
||||
|
||||
Dans cette démonstration, l'objectif est de comprendre les concepts fondamentaux de la programmation asynchrone en JavaScript.
|
||||
|
||||
Nous allons simuler la préparation d'un plat de pâtes pour illustrer :
|
||||
1. La modularité du code avec une "API".
|
||||
2. L'utilisation des **Promises** pour gérer des opérations qui prennent du temps.
|
||||
3. La syntaxe `async/await` pour écrire un code asynchrone clair et lisible.
|
||||
4. L'optimisation des tâches parallèles avec `Promise.all`.
|
||||
5. La gestion des erreurs avec `try/catch`.
|
||||
|
||||
### Prérequis
|
||||
|
||||
* Avoir `Node.js` installé sur votre machine.
|
||||
|
||||
---
|
||||
|
||||
### Étape 1 : Callback hell (l'enfer des callbacks)
|
||||
|
||||
Observez le code fourni dans le fichier `callback_hell.js`.
|
||||
Ce code simule l'interrogation d'une API pour récupérer des données utilisateur, les droits de cet utilisateur, ainsi que ses messages de blog.
|
||||
Chaque étape dépend de la précédente, ce qui crée une pyramide de callbacks difficile à lire et à maintenir.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Pyramide de Callbacks"
|
||||
A[getUser] --> B{"Callback 1"};
|
||||
B --> C[getPermissions];
|
||||
C --> D{"Callback 2"};
|
||||
D --> E[getPosts];
|
||||
E --> F{"Callback 3"};
|
||||
F --> G[...];
|
||||
end
|
||||
```
|
||||
|
||||
Dans la suite, nous utiliserons une approche plus moderne et plus propre pour gérer ce type de scénario.
|
||||
|
||||
---
|
||||
|
||||
### Étape 2 : Création de notre API de Cuisine
|
||||
|
||||
Notre premier fichier, `api_cuisson.js`, contiendra toutes les fonctions de base pour préparer nos pâtes. Chaque fonction simulera une action (comme chauffer de l'eau) et retournera une `Promise`. Une `Promise` est un objet qui représente la complétion (ou l'échec) future d'une opération asynchrone.
|
||||
|
||||
**Analyse de l'existant :**
|
||||
* **`config`** : un objet simple pour centraliser les durées de chaque étape.
|
||||
* **`new Promise((resolve, reject) => { ... })`** : c'est le cœur d'une fonction asynchrone.
|
||||
* le code à l'intérieur (ici, un `setTimeout`) s'exécute.
|
||||
* quand l'opération réussit, on appelle `resolve()` pour signaler que la promesse est tenue.
|
||||
* en cas d'erreur (comme dans `preparerSauce`), on appelle `reject()` pour signaler que la promesse est rompue.
|
||||
* **`export`** : ce mot-clé permet à d'autres fichiers (comme `index.js`) d'importer et d'utiliser ces fonctions.
|
||||
|
||||
Une promesse peut avoir trois états : en attente (pending), tenue (fulfilled) ou rompue (rejected).
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A["pending 'état initial'"] --> B["fulfilled 'réussie'"];
|
||||
A --> C["rejected 'échouée'"];
|
||||
B --> D["Données retournées via .then"];
|
||||
C --> E["Erreur gérée via .catch"];
|
||||
```
|
||||
|
||||
Le but est de compléter les TODO dans ce fichier, sur le modèle de la fonction `chaufferEau`.
|
||||
|
||||
---
|
||||
|
||||
### Étape 3 : Orchestration avec `async/await`
|
||||
|
||||
Maintenant que notre "API" est prête, nous allons l'utiliser dans `index.js` pour créer nos scénarios de préparation.
|
||||
Nous utiliserons la syntaxe `async/await` qui est une manière moderne et très lisible de travailler avec les Promises.
|
||||
|
||||
**Analyse de l'existant :**
|
||||
* **`async function`** : déclare une fonction asynchrone. Seules les fonctions `async` peuvent utiliser le mot-clé `await`.
|
||||
* **`await`** : met en pause l'exécution de la fonction `async` et attend que la `Promise` soit résolue. Le code devient aussi simple à lire que du code synchrone !
|
||||
* **`try/catch`** : c'est le mécanisme standard pour gérer les erreurs. Si une `Promise` dans le bloc `try` est "rejetée" (`reject`), l'exécution saute directement au bloc `catch`.
|
||||
* **`Promise.all([...])`** : c'est l'outil d'optimisation ! Il prend un tableau de Promises et attend qu'elles soient **toutes** terminées avec succès. C'est parfait pour les tâches qui ne dépendent pas les unes des autres (on peut faire la sauce PENDANT que l'eau chauffe).
|
||||
|
||||
Le but est de compléter les TODO dans ce fichier pour implémenter les deux scénarios :
|
||||
1. Préparation séquentielle (non optimisée) : chaque étape attend la fin de la précédente. On souhaite d'abord chauffer l'eau, puis préparer la sauce, ensuite cuire les pâtes, et enfin mélanger le tout.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[chaufferEau] --> B[preparerSauce];
|
||||
B --> C[cuirePates];
|
||||
C --> D[melangerPatesEtSauce];
|
||||
D --> E[Plat prêt];
|
||||
```
|
||||
|
||||
2. Préparation optimisée (avec `Promise.all`) : chauffer l'eau et préparer la sauce en parallèle. On souhaite lancer ces deux tâches en même temps, puis attendre qu'elles soient toutes les deux terminées avec succès avant de cuire les pâtes et de mélanger le tout.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Parallèle: Promise.all"
|
||||
A[chaufferEau]
|
||||
B[preparerSauce]
|
||||
end
|
||||
|
||||
subgraph "Synchronisation"
|
||||
J["Attente de la fin des deux tâches"]
|
||||
end
|
||||
|
||||
A --> J;
|
||||
B --> J;
|
||||
|
||||
J --> C[cuirePates];
|
||||
C --> D[melangerPatesEtSauce];
|
||||
D --> E[Plat prêt];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Étape 4 : Exécution et analyse des résultats
|
||||
|
||||
Tout est en place ! Il est temps de lancer notre script et de voir la différence entre une préparation séquentielle et une préparation optimisée.
|
||||
|
||||
Placez-vous sur le fichier `index.js` et exécutez le fichier via le bouton "Run" de votre IDE.
|
||||
|
||||
Vous devriez avoir un résultat console similaire à celui-ci :
|
||||
|
||||
```
|
||||
============================================================
|
||||
Démonstration de code JavaScript asynchrone : la cuisine
|
||||
============================================================
|
||||
|
||||
--- SCÉNARIO 1 : préparation séquentielle (non optimisée) ---
|
||||
Lancement de la préparation pour : Spaghetti Bolognaise.
|
||||
- L'eau chauffe...
|
||||
-> L'eau est à ébullition.
|
||||
- Préparation de la sauce Bolognaise...
|
||||
-> Sauce Bolognaise prête.
|
||||
- Cuisson des Spaghetti...
|
||||
-> Spaghetti cuites.
|
||||
- Mélange des pâtes et de la sauce...
|
||||
-> Plat de Spaghetti sauce Bolognaise servi. Bon appétit !
|
||||
|
||||
SUCCÈS : votre 'Plat de Spaghetti sauce Bolognaise' est prêt !
|
||||
Temps total (séquentiel): 4.152s
|
||||
|
||||
------------------------------------------------------------
|
||||
|
||||
|
||||
--- SCÉNARIO 2 : préparation optimisée (avec Promise.all) ---
|
||||
Lancement de la préparation pour : Penne Arrabbiata.
|
||||
-> Lancement en parallèle du chauffage de l'eau et de la préparation de la sauce...
|
||||
- L'eau chauffe...
|
||||
- Préparation de la sauce Arrabbiata...
|
||||
-> Sauce Arrabbiata prête.
|
||||
-> L'eau est à ébullition.
|
||||
-> Eau bouillante et sauce prête ! On peut lancer la cuisson des pâtes.
|
||||
- Cuisson des Penne...
|
||||
-> Penne cuites.
|
||||
- Mélange des pâtes et de la sauce...
|
||||
-> Plat de Penne sauce Arrabbiata servi. Bon appétit !
|
||||
|
||||
SUCCÈS (optimisé) : votre 'Plat de Penne sauce Arrabbiata' est prêt !
|
||||
Temps total (optimisé): 2.921s
|
||||
```
|
||||
|
||||
### Conclusion
|
||||
|
||||
Remarquez la différence de **temps total** entre les deux scénarios.
|
||||
Dans le scénario 2, le temps total s'approche de celui de la tâche parallèle la plus longue (`chaufferEau`) plus le reste des étapes, ce qui est bien plus rapide que d'additionner toutes les durées.
|
||||
81
api_cuisson.js
Normal file
81
api_cuisson.js
Normal file
@@ -0,0 +1,81 @@
|
||||
// ===================================================================
|
||||
// API de Cuisine pour cuisson de pâtes - Fonctions Asynchrones
|
||||
//
|
||||
// Chaque fonction simule une étape de la préparation et retourne
|
||||
// une Promise. Cette approche modulaire sépare la logique de
|
||||
// chaque action de son orchestration.
|
||||
// ===================================================================
|
||||
|
||||
/**
|
||||
* @typedef {object} ConfigCuisson
|
||||
* @property {number} TEMPS_EBULLITION - Temps en ms pour que l'eau arrive à ébullition.
|
||||
* @property {number} TEMPS_CUISSON_SAUCE - Temps en ms pour préparer la sauce.
|
||||
* @property {number} TEMPS_CUISSON_PATES - Temps en ms pour cuire les pâtes.
|
||||
* @property {number} TEMPS_MELANGE - Temps en ms pour mélanger et servir.
|
||||
*/
|
||||
|
||||
/** @type {ConfigCuisson} */
|
||||
export const config = {
|
||||
TEMPS_EBULLITION: 1500,
|
||||
TEMPS_CUISSON_SAUCE: 1200,
|
||||
TEMPS_CUISSON_PATES: 1000,
|
||||
TEMPS_MELANGE: 400,
|
||||
};
|
||||
|
||||
// --- API de la cuisine (fonctions "promisifiées") ---
|
||||
|
||||
export const apiCuisson = {
|
||||
/**
|
||||
* Simule la mise à ébullition de l'eau.
|
||||
* @returns {Promise<string>} Promesse qui se résout avec 'eau-bouillante'.
|
||||
*/
|
||||
chaufferEau() {
|
||||
return new Promise((resolve) => {
|
||||
console.log(" - L'eau chauffe...");
|
||||
setTimeout(() => {
|
||||
console.log(" -> L'eau est à ébullition.");
|
||||
resolve('eau-bouillante');
|
||||
}, config.TEMPS_EBULLITION);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Simule la préparation de la sauce.
|
||||
* @param {string} typeSauce - Le type de sauce à préparer.
|
||||
* @returns {Promise<string>} Promesse qui se résout avec 'sauce-prete'. Rejette si aucun type n'est fourni.
|
||||
*/
|
||||
preparerSauce(typeSauce) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(` - Préparation de la sauce ${typeSauce}...`);
|
||||
// TODO : compléter comme l'exemple ci-dessus. Gérer le cas où typeSauce est vide ou null en rejetant la promesse avec une erreur.
|
||||
// ... votre code ici ...
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Simule la cuisson des pâtes.
|
||||
* @param {string} typePates - Le type de pâtes à cuire.
|
||||
* @returns {Promise<string>} Promesse qui se résout avec 'pates-cuites'.
|
||||
*/
|
||||
cuirePates(typePates) {
|
||||
return new Promise((resolve) => {
|
||||
console.log(` - Cuisson des ${typePates}...`);
|
||||
// TODO : compléter comme l'exemple chaufferEau ci-dessus.
|
||||
// ... votre code ici ...
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Simule le mélange final et le service.
|
||||
* @param {string} typePates - Le type de pâtes.
|
||||
* @param {string} typeSauce - Le type de sauce.
|
||||
* @returns {Promise<string>} Promesse qui se résout avec le plat final.
|
||||
*/
|
||||
melangerEtServir(typePates, typeSauce) {
|
||||
return new Promise((resolve) => {
|
||||
console.log(" - Mélange des pâtes et de la sauce...");
|
||||
// TODO : compléter comme l'exemple chaufferEau ci-dessus.
|
||||
// ... votre code ici ...
|
||||
});
|
||||
}
|
||||
};
|
||||
76
callback_hell.js
Normal file
76
callback_hell.js
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// Exemple de "Callback Hell" en JavaScript
|
||||
// Comme on peut le constater, chaque opération dépend du résultat de la précédente.
|
||||
// Cela conduit à une indentation profonde et rend le code difficile à lire et à maintenir.
|
||||
//
|
||||
|
||||
// --- Fonctions asynchrones simulées ---
|
||||
|
||||
// 1. Récupère un utilisateur
|
||||
function getUser(id, callback) {
|
||||
console.log("Recherche de l'utilisateur...");
|
||||
setTimeout(() => {
|
||||
// Simule un succès ou une erreur aléatoire
|
||||
if (id === 1) {
|
||||
const user = { id: 1, name: "Alice" };
|
||||
callback(null, user); // Pas d'erreur, on renvoie l'utilisateur
|
||||
} else {
|
||||
callback("Utilisateur non trouvé", null); // Erreur
|
||||
}
|
||||
}, 1000); // Attend 1 seconde
|
||||
}
|
||||
|
||||
// 2. Récupère les permissions de l'utilisateur
|
||||
function getPermissions(user, callback) {
|
||||
console.log(`Recherche des permissions pour ${user.name}...`);
|
||||
setTimeout(() => {
|
||||
const permissions = { level: "admin" };
|
||||
callback(null, permissions);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 3. Récupère les articles de blog de l'utilisateur
|
||||
function getPosts(user, callback) {
|
||||
console.log(`Recherche des articles de ${user.name}...`);
|
||||
setTimeout(() => {
|
||||
const posts = ["Article 1", "Article 2", "Article 3"];
|
||||
callback(null, posts);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
// --- Le Début du Callback Hell ---
|
||||
|
||||
console.log("Début du processus...");
|
||||
|
||||
getUser(1, (error, user) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
console.log("Utilisateur trouvé :", user);
|
||||
|
||||
// Une fois qu'on a l'utilisateur, on lance la 2ème opération
|
||||
getPermissions(user, (error, permissions) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
console.log("Permissions trouvées :", permissions);
|
||||
|
||||
// Une fois qu'on a les permissions, on lance la 3ème opération
|
||||
getPosts(user, (error, posts) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
console.log("Articles trouvés :", posts);
|
||||
|
||||
// On a enfin toutes nos données
|
||||
console.log("\n--- Résultat final ---");
|
||||
console.log(`Nom de l'utilisateur : ${user.name}`);
|
||||
console.log(`Niveau de permission : ${permissions.level}`);
|
||||
console.log(`Derniers articles : ${posts.join(', ')}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
110
index.js
Normal file
110
index.js
Normal file
@@ -0,0 +1,110 @@
|
||||
// ===================================================================
|
||||
// Orchestration de la préparation d'un plat de pâtes
|
||||
//
|
||||
// Objectifs :
|
||||
// 1. utiliser `async/await` pour écrire un code asynchrone lisible.
|
||||
// 2. gérer les erreurs potentielles avec un bloc `try/catch`.
|
||||
// 3. optimiser le temps de préparation avec `Promise.all`.
|
||||
// ===================================================================
|
||||
|
||||
import { apiCuisson } from './api_cuisson.js';
|
||||
|
||||
/**
|
||||
* Scénario 1 : préparation séquentielle.
|
||||
* Chaque étape attend la fin de la précédente. C'est simple mais lent.
|
||||
*/
|
||||
async function preparationSequentielle() {
|
||||
console.log("\n--- SCÉNARIO 1 : préparation séquentielle (non optimisée) ---");
|
||||
console.time("Temps total (séquentiel)");
|
||||
try {
|
||||
const typePates = "Spaghetti";
|
||||
const typeSauce = "Bolognaise";
|
||||
|
||||
console.log(`Lancement de la préparation pour : ${typePates} ${typeSauce}.`);
|
||||
|
||||
// TODO : utiliser l'API de cuisson pour chaque étape, en séquence.
|
||||
// ... votre code ici ...
|
||||
|
||||
console.log(`\nSUCCÈS : votre '${platFinal}' est prêt !`);
|
||||
|
||||
} catch (error) {
|
||||
console.error("ERREUR : La préparation a échoué.", error.message);
|
||||
} finally {
|
||||
// Ce bloc s'exécute toujours, que ce soit un succès ou un échec.
|
||||
console.timeEnd("Temps total (séquentiel)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scénario 2 : préparation optimisée.
|
||||
* Les tâches indépendantes (chauffer l'eau, préparer la sauce) sont
|
||||
* lancées en parallèle pour gagner du temps.
|
||||
*/
|
||||
async function preparationOptimisee() {
|
||||
console.log("\n--- SCÉNARIO 2 : préparation optimisée (avec Promise.all) ---");
|
||||
console.time("Temps total (optimisé)");
|
||||
try {
|
||||
const typePates = "Penne";
|
||||
const typeSauce = "Arrabbiata";
|
||||
|
||||
console.log(`Lancement de la préparation pour : ${typePates} ${typeSauce}.`);
|
||||
|
||||
console.log("-> Lancement en parallèle du chauffage de l'eau et de la préparation de la sauce...");
|
||||
|
||||
// TODO : utiliser Promise.all pour lancer les tâches indépendantes en parallèle.
|
||||
// ... votre code ici ...
|
||||
|
||||
console.log("-> Eau bouillante et sauce prête ! On peut lancer la cuisson des pâtes.");
|
||||
|
||||
// TODO : cuire les pâtes et finaliser le plat.
|
||||
// ... votre code ici ...
|
||||
|
||||
console.log(`\nSUCCÈS (optimisé) : votre '${platFinal}' est prêt !`);
|
||||
|
||||
} catch (error) {
|
||||
console.error("ERREUR (optimisé) : la préparation a échoué.", error.message);
|
||||
} finally {
|
||||
console.timeEnd("Temps total (optimisé)");
|
||||
}
|
||||
}
|
||||
|
||||
// Petit utilitaire pour créer une pause non-bloquante
|
||||
const delai = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* Une tâche de fond asynchrone qui s'exécute en boucle.
|
||||
* @param {number} nb_tours - Nombre d'itérations avant d'arrêter.
|
||||
*/
|
||||
async function activiteAsyncEnBoucle(nb_tours = 10) {
|
||||
|
||||
let compteur = 0;
|
||||
console.log("Lancement de l'activité ASYNC en boucle...");
|
||||
|
||||
// La boucle s'exécute un nombre de fois défini par nb_tours
|
||||
while ( compteur < nb_tours ) {
|
||||
console.log(`L'application est vivante ! (tâche async tic: ${++compteur})`);
|
||||
// On attend de manière non-bloquante
|
||||
await delai(500);
|
||||
}
|
||||
|
||||
console.log("Tâche async en boucle terminée.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Point d'entrée principal du script.
|
||||
*/
|
||||
async function main() {
|
||||
console.log("=".repeat(60));
|
||||
console.log("Démonstration de code JavaScript asynchrone : la cuisine");
|
||||
console.log("=".repeat(60));
|
||||
|
||||
await preparationSequentielle();
|
||||
//await Promise.all([activiteAsyncEnBoucle(), preparationSequentielle()]);
|
||||
|
||||
console.log("\n" + "-".repeat(60) + "\n");
|
||||
await preparationOptimisee();
|
||||
}
|
||||
|
||||
// Lancement de la démonstration
|
||||
main();
|
||||
Reference in New Issue
Block a user