# Chapitre 3 : Programmation Asynchrone ## Introduction En JavaScript, de nombreuses opérations sont **asynchrones** : - Appels API (fetch) - Lecture de fichiers - Timers (setTimeout) - Accès à la base de données locale (IndexedDB) Contrairement à PHP où le code s'exécute ligne par ligne, JavaScript peut lancer une opération et continuer sans attendre le résultat. ## Le problème ```javascript // Ce code ne fait pas ce que vous pensez! console.log("1. Début"); setTimeout(() => { console.log("2. Dans le timeout"); }, 1000); console.log("3. Fin"); // Affiche : // "1. Début" // "3. Fin" // "2. Dans le timeout" (1 seconde plus tard) ``` ## Les Promises Une Promise représente une valeur qui sera disponible dans le futur. ### États d'une Promise - **pending** : en attente - **fulfilled** : résolue avec succès (contient une valeur) - **rejected** : rejetée (contient une erreur) ### Créer une Promise ```javascript const myPromise = new Promise((resolve, reject) => { // Opération asynchrone simulée setTimeout(() => { const success = true; if (success) { resolve("Données reçues"); // Succès } else { reject(new Error("Échec")); // Erreur } }, 1000); }); ``` ### Consommer une Promise avec .then() et .catch() ```javascript myPromise .then(result => { console.log("Succès:", result); }) .catch(error => { console.log("Erreur:", error.message); }); // Chaînage de .then() fetchUser(1) .then(user => fetchPosts(user.id)) .then(posts => console.log(posts)) .catch(error => console.log("Erreur:", error)); ``` ## async/await `async/await` est une syntaxe plus lisible pour travailler avec les Promises. ### Syntaxe de base ```javascript // Fonction async async function fetchData() { const response = await fetch("https://api.example.com/data"); const data = await response.json(); return data; } // Arrow function async const fetchData = async () => { const response = await fetch("https://api.example.com/data"); const data = await response.json(); return data; }; ``` ### Règles importantes 1. `await` ne peut être utilisé que dans une fonction `async` 2. Une fonction `async` retourne toujours une Promise 3. `await` "pause" l'exécution jusqu'à ce que la Promise soit résolue ### Exemple comparatif ```javascript // Avec .then() function getUserPosts(userId) { return fetchUser(userId) .then(user => fetchPosts(user.id)) .then(posts => { console.log(posts); return posts; }); } // Avec async/await (plus lisible) async function getUserPosts(userId) { const user = await fetchUser(userId); const posts = await fetchPosts(user.id); console.log(posts); return posts; } ``` ## Gestion des erreurs ### Avec try/catch ```javascript async function fetchData() { try { const response = await fetch("https://api.example.com/data"); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const data = await response.json(); return data; } catch (error) { console.error("Erreur:", error.message); // Gérer l'erreur (afficher un message, retourner une valeur par défaut, etc.) return null; } } ``` ### Pattern courant en React ```javascript const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const loadData = async () => { setLoading(true); setError(null); try { const response = await fetch("/api/items"); const result = await response.json(); setData(result); } catch (err) { setError(err.message); } finally { setLoading(false); } }; ``` ## Exécution parallèle vs séquentielle ### Séquentiel (un après l'autre) ```javascript async function sequential() { const user = await fetchUser(1); // Attend... const posts = await fetchPosts(1); // Puis attend... const comments = await fetchComments(1); // Puis attend... // Temps total = temps1 + temps2 + temps3 } ``` ### Parallèle (en même temps) ```javascript async function parallel() { // Lance les 3 requêtes en même temps const [user, posts, comments] = await Promise.all([ fetchUser(1), fetchPosts(1), fetchComments(1) ]); // Temps total = max(temps1, temps2, temps3) } ``` ### Promise.all vs Promise.allSettled ```javascript // Promise.all - échoue si UNE promise échoue try { const results = await Promise.all([promise1, promise2, promise3]); } catch (error) { // Une des promises a échoué } // Promise.allSettled - attend toutes, même si certaines échouent const results = await Promise.allSettled([promise1, promise2, promise3]); // results = [ // { status: "fulfilled", value: ... }, // { status: "rejected", reason: Error }, // { status: "fulfilled", value: ... } // ] ``` ## Comparaison avec PHP ```php // PHP - synchrone par défaut $user = file_get_contents("https://api.example.com/user/1"); $posts = file_get_contents("https://api.example.com/posts/1"); // Chaque ligne attend que la précédente soit terminée ``` ```javascript // JavaScript - asynchrone const user = await fetch("https://api.example.com/user/1"); const posts = await fetch("https://api.example.com/posts/1"); // Similaire grâce à await, mais sous le capot c'est asynchrone ``` ## Pièges courants ### 1. Oublier await ```javascript // INCORRECT async function getData() { const data = fetch("/api/data"); // Oublié await! console.log(data); // Promise { } - pas les données! } // CORRECT async function getData() { const response = await fetch("/api/data"); const data = await response.json(); console.log(data); } ``` ### 2. await dans une boucle (séquentiel non voulu) ```javascript // LENT - chaque itération attend la précédente async function slow() { for (const id of ids) { const data = await fetchItem(id); // Séquentiel! } } // RAPIDE - toutes les requêtes en parallèle async function fast() { const promises = ids.map(id => fetchItem(id)); const results = await Promise.all(promises); } ``` ### 3. Ne pas gérer les erreurs ```javascript // DANGEREUX - erreur non gérée async function riskyCode() { const data = await fetchData(); // Si ça échoue? } // SÛRE - erreur gérée async function safeCode() { try { const data = await fetchData(); } catch (error) { console.error("Erreur gérée:", error); } } ``` ## Exercices ### Exercice 1 : Convertir en async/await Convertir ce code utilisant `.then()` : ```javascript function getUser(id) { return fetch(`/api/users/${id}`) .then(response => response.json()) .then(user => { console.log(user); return user; }) .catch(error => { console.error("Erreur:", error); return null; }); } ``` **Solution :** ```javascript async function getUser(id) { try { const response = await fetch(`/api/users/${id}`); const user = await response.json(); console.log(user); return user; } catch (error) { console.error("Erreur:", error); return null; } } ``` ### Exercice 2 : Parallélisation Optimiser ce code pour exécuter les requêtes en parallèle : ```javascript async function loadDashboard(userId) { const user = await fetchUser(userId); const orders = await fetchOrders(userId); const notifications = await fetchNotifications(userId); return { user, orders, notifications }; } ``` **Solution :** ```javascript async function loadDashboard(userId) { const [user, orders, notifications] = await Promise.all([ fetchUser(userId), fetchOrders(userId), fetchNotifications(userId) ]); return { user, orders, notifications }; } ``` ## Points clés à retenir 1. **Promise** : représente une valeur future (pending → fulfilled/rejected) 2. **async/await** : syntaxe lisible pour les Promises 3. **await** : "pause" l'exécution jusqu'à résolution 4. **try/catch** : gestion des erreurs avec async/await 5. **Promise.all** : exécuter plusieurs Promises en parallèle 6. **Toujours gérer les erreurs** avec try/catch [[:15_training:module1-javascript-es6:fonctions|← Chapitre précédent]] | [[:15_training:module1-javascript-es6:start|Retour au module]] | [[:15_training:module1-javascript-es6:modules-es6|Chapitre suivant : Modules ES6 →]]