Cours JavaScript et TypeScript · Module 4: JavaScript asynchrone
Fetch API
Dans cette leçon18
Dans le Parc Jurassique moderne, en plus des systèmes locaux de surveillance et de contrôle, nous devons aussi communiquer avec des systèmes et des API externes. Que ce soit pour consulter les prévisions météo du parc, synchroniser les données avec des bases de données mondiales de dinosaures ou s'intégrer aux systèmes de réservation des visiteurs - tout cela nécessite une méthode fiable pour effectuer des requêtes HTTP. Dans cet exercice, nous allons découvrir la Fetch API, l'interface moderne pour effectuer des requêtes réseau en JavaScript.
Qu'est-ce que la Fetch API ?
La Fetch API est une interface JavaScript moderne et intégrée pour effectuer des requêtes HTTP. Elle remplace les approches plus anciennes comme XMLHttpRequest, en offrant un ensemble d'outils plus ergonomique et plus puissant, basé sur les Promises.
Principaux avantages de la Fetch API :
- Utilise les Promises, ce qui facilite la gestion des opérations asynchrones
- A une syntaxe plus simple et plus intuitive que XMLHttpRequest
- Offre un meilleur contrôle sur les requêtes HTTP
- Permet de chaîner facilement des opérations asynchrones
- Est un standard des navigateurs et est disponible dans Node.js (à partir de la version 18)
Utilisation basique de Fetch
La syntaxe basique de Fetch est très simple :
1fetch(url)
2 .then(response => response.json())
3 .then(data => console.log(data))
4 .catch(error => console.error('Erreur :', error));Voyons comment nous pourrions utiliser Fetch dans notre Parc Jurassique :
1// Récupération des données d'un dinosaure depuis l'API
2function fetchDinosaurData(id) {
3 return fetch(`https://api.jurassic-park.com/dinosaurs/${id}`)
4 .then(response => {
5 // Vérifier que la réponse est un succès
6 if (!response.ok) {
7 throw new Error(`Erreur HTTP : ${response.status}`);
8 }
9 return response.json();
10 })
11 .then(data => {
12 console.log(`Données du dinosaure récupérées : ${data.name}`);
13 return data;
14 })
15 .catch(error => {
16 console.error(`Échec de la récupération des données du dinosaure #${id} :`, error);
17 throw error;
18 });
19}
20
21// Utilisation de la fonction
22fetchDinosaurData(42)
23 .then(dino => {
24 console.log(`Dinosaure #42 : ${dino.name}, Espèce : ${dino.species}`);
25 updateInfoPanel(dino);
26 })
27 .catch(error => {
28 displayErrorMessage(`Échec de la récupération des infos du dinosaure : ${error.message}`);
29 });Effectuer différents types de requêtes HTTP
Fetch effectue une requête GET par défaut, mais nous pouvons facilement spécifier un autre type de requête et envoyer des données :
1// Mise à jour des données d'un dinosaure avec une requête PUT
2async function updateDinosaurData(id, newData) {
3 try {
4 const response = await fetch(`https://api.jurassic-park.com/dinosaurs/${id}`, {
5 method: 'PUT',
6 headers: {
7 'Content-Type': 'application/json',
8 'Authorization': `Bearer ${apiToken}`
9 },
10 body: JSON.stringify(newData)
11 });
12
13 if (!response.ok) {
14 throw new Error(`Erreur de mise à jour : ${response.status}`);
15 }
16
17 const updatedDinosaur = await response.json();
18 console.log(`Données du dinosaure #${id} mises à jour`);
19 return updatedDinosaur;
20 } catch (error) {
21 console.error(`Erreur lors de la mise à jour du dinosaure #${id} :`, error);
22 throw error;
23 }
24}
25
26// Création d'un nouvel enregistrement de dinosaure avec une requête POST
27async function addNewDinosaur(data) {
28 try {
29 const response = await fetch('https://api.jurassic-park.com/dinosaurs', {
30 method: 'POST',
31 headers: {
32 'Content-Type': 'application/json',
33 'Authorization': `Bearer ${apiToken}`
34 },
35 body: JSON.stringify(data)
36 });
37
38 if (!response.ok) {
39 throw new Error(`Erreur lors de l'ajout du dinosaure : ${response.status}`);
40 }
41
42 const newDinosaur = await response.json();
43 console.log(`Nouveau dinosaure #${newDinosaur.id} ajouté`);
44 return newDinosaur;
45 } catch (error) {
46 console.error('Erreur lors de l\'ajout du dinosaure :', error);
47 throw error;
48 }
49}
50
51// Suppression d'un dinosaure avec une requête DELETE
52async function deleteDinosaur(id) {
53 try {
54 const response = await fetch(`https://api.jurassic-park.com/dinosaurs/${id}`, {
55 method: 'DELETE',
56 headers: {
57 'Authorization': `Bearer ${apiToken}`
58 }
59 });
60
61 if (!response.ok) {
62 throw new Error(`Erreur lors de la suppression du dinosaure : ${response.status}`);
63 }
64
65 // Pour DELETE, le serveur peut ne renvoyer aucune donnée
66 if (response.status !== 204) { // No Content
67 const data = await response.json();
68 console.log(`Dinosaure #${id} supprimé :`, data);
69 return data;
70 }
71
72 console.log(`Dinosaure #${id} supprimé`);
73 return { success: true, id };
74 } catch (error) {
75 console.error(`Erreur lors de la suppression du dinosaure #${id} :`, error);
76 throw error;
77 }
78}L'objet Response
Lorsque nous appelons fetch(), il retourne une Promise qui se résout en un objet Response. Cet objet contient toutes les informations sur la réponse HTTP :
1fetch('https://api.jurassic-park.com/status')
2 .then(response => {
3 console.log('Statut :', response.status); // ex. 200
4 console.log('Statut OK :', response.ok); // true pour les statuts 200-299
5 console.log('Type de contenu :', response.headers.get('content-type'));
6 console.log('Tous les en-têtes :', [...response.headers.entries()]);
7
8 return response.json(); // Analyser la réponse en JSON
9 })
10 .then(data => {
11 console.log('Données :', data);
12 });L'objet Response propose différentes méthodes pour traiter les données de la réponse :
| Méthode | Description |
|---|---|
| response.json() | Analyse le contenu en JSON et retourne une Promise avec le résultat |
| response.text() | Retourne une Promise avec le contenu sous forme de texte |
| response.blob() | Retourne une Promise avec le contenu sous forme d'objet Blob (données binaires) |
| response.formData() | Analyse le contenu en FormData |
| response.arrayBuffer() | Retourne une Promise avec le contenu sous forme d'ArrayBuffer |
Il est important de retenir qu'on ne peut appeler qu'une seule de ces méthodes sur un objet Response. Le corps de la réponse ne peut être traité qu'une seule fois - tenter d'appeler response.json() plusieurs fois provoquera une erreur.
Gestion des erreurs dans Fetch
Une caractéristique importante de la Fetch API est que la Promise retournée par fetch() ne sera pas rejetée pour les erreurs HTTP comme 404 ou 500. Elle ne sera rejetée que pour les problèmes réseau comme une absence de connexion.
Pour gérer correctement les erreurs HTTP, il faut vérifier la propriété response.ok ou le code response.status :
1fetch('https://api.jurassic-park.com/dinosaurs/999')
2 .then(response => {
3 if (!response.ok) {
4 throw new Error(`Erreur HTTP : ${response.status}`);
5 }
6 return response.json();
7 })
8 .then(data => {
9 console.log('Données :', data);
10 })
11 .catch(error => {
12 console.error('Erreur :', error.message);
13 // Ici on peut gérer à la fois les erreurs HTTP et les erreurs réseau
14 });Tu peux étendre cette logique pour mieux gérer différents codes de statut :
1fetch('https://api.jurassic-park.com/dinosaurs/999')
2 .then(response => {
3 if (response.status === 404) {
4 throw new Error('Dinosaure introuvable');
5 } else if (response.status === 401) {
6 throw new Error('Non autorisé - merci de te reconnecter');
7 } else if (response.status === 403) {
8 throw new Error('Aucune permission pour cette opération');
9 } else if (!response.ok) {
10 throw new Error(`Erreur HTTP : ${response.status}`);
11 }
12 return response.json();
13 })
14 .then(/* ... */)
15 .catch(/* ... */);Options de configuration de Fetch
Fetch accepte un second argument optionnel qui permet de personnaliser la requête :
1fetch(url, {
2 method: 'POST', // Méthode HTTP (GET, POST, PUT, DELETE, etc.)
3 headers: { // En-têtes HTTP
4 'Content-Type': 'application/json',
5 'Authorization': 'Bearer token123'
6 },
7 body: JSON.stringify(data), // Corps de la requête (pour POST, PUT, etc.)
8 mode: 'cors', // Mode CORS
9 credentials: 'include', // Inclure ou non les cookies
10 cache: 'no-cache', // Stratégie de gestion du cache
11 redirect: 'follow', // Comment gérer les redirections
12 referrer: 'no-referrer', // En-tête Referrer
13 signal: abortController.signal // Pour annuler la requête
14})Exemple avec la plupart des options
1async function makeComplexRequest() {
2 const abortController = new AbortController();
3 const signal = abortController.signal;
4
5 // Définir un timeout pour la requête
6 const timeout = setTimeout(() => {
7 abortController.abort();
8 }, 5000); // 5 secondes
9
10 try {
11 const response = await fetch('https://api.jurassic-park.com/data', {
12 method: 'POST',
13 headers: {
14 'Content-Type': 'application/json',
15 'Authorization': `Bearer ${apiToken}`,
16 'X-Jurassic-Version': '1.2'
17 },
18 body: JSON.stringify({
19 query: 'velociraptor',
20 limit: 10,
21 withDetails: true
22 }),
23 mode: 'cors',
24 credentials: 'include', // Inclure les cookies
25 cache: 'no-cache', // Ne pas utiliser le cache
26 redirect: 'follow', // Suivre automatiquement les redirections
27 referrer: 'no-referrer',
28 signal // Pour annuler la requête
29 });
30
31 clearTimeout(timeout);
32
33 if (!response.ok) {
34 throw new Error(`Erreur HTTP : ${response.status}`);
35 }
36
37 return await response.json();
38 } catch (error) {
39 clearTimeout(timeout);
40
41 if (error.name === 'AbortError') {
42 console.error('Requête annulée après expiration du délai');
43 throw new Error('Délai de la requête dépassé');
44 }
45
46 console.error('Erreur de requête :', error);
47 throw error;
48 }
49}Annuler des requêtes avec AbortController
AbortController est une interface qui permet d'annuler des requêtes Fetch. Elle est particulièrement utile pour implémenter des timeouts ou lorsque l'utilisateur souhaite annuler une requête longue :
1function fetchLargeDinosaurData(query) {
2 // Créer un contrôleur d'annulation
3 const controller = new AbortController();
4 const { signal } = controller;
5
6 // Définir un timeout
7 const timeoutId = setTimeout(() => {
8 controller.abort();
9 }, 10000); // 10 secondes
10
11 // Bouton d'annulation pour l'utilisateur
12 const cancelButton = document.getElementById('cancel-button');
13 cancelButton.addEventListener('click', () => {
14 controller.abort();
15 clearTimeout(timeoutId);
16 cancelButton.disabled = true;
17 cancelButton.textContent = 'Annulé';
18 });
19
20 // Effectuer la requête avec le signal d'annulation
21 return fetch(`https://api.jurassic-park.com/search?q=${query}`, { signal })
22 .then(response => {
23 clearTimeout(timeoutId);
24 if (!response.ok) {
25 throw new Error(`Erreur HTTP : ${response.status}`);
26 }
27 return response.json();
28 })
29 .then(data => {
30 cancelButton.disabled = true;
31 cancelButton.textContent = 'Terminé';
32 return data;
33 })
34 .catch(error => {
35 clearTimeout(timeoutId);
36
37 if (error.name === 'AbortError') {
38 console.log('La requête a été annulée');
39 return { cancelled: true };
40 }
41
42 throw error;
43 });
44}Requêtes parallèles avec Promise.all
Parfois, nous devons effectuer plusieurs requêtes en même temps :
1async function fetchPaddockData(paddockId) {
2 try {
3 const dinosaursPromise = fetch(`https://api.jurassic-park.com/paddocks/${paddockId}/dinosaurs`).then(r => r.json());
4 const statusPromise = fetch(`https://api.jurassic-park.com/paddocks/${paddockId}/status`).then(r => r.json());
5 const historyPromise = fetch(`https://api.jurassic-park.com/paddocks/${paddockId}/history`).then(r => r.json());
6
7 const [dinosaurs, status, history] = await Promise.all([
8 dinosaursPromise,
9 statusPromise,
10 historyPromise
11 ]);
12
13 return { paddockId, dinosaurs, status, history, updatedAt: new Date() };
14 } catch (error) {
15 console.error(`Erreur lors de la récupération des données de l'enclos #${paddockId} :`, error);
16 throw error;
17 }
18}Gérer différents formats de réponse
Fetch peut traiter différents formats de données, pas seulement le JSON :
1// Texte brut
2fetch('https://api.jurassic-park.com/logs')
3 .then(response => response.text())
4 .then(text => {
5 console.log('Logs :', text);
6 document.getElementById('logs').textContent = text;
7 });
8
9// Données binaires (images, fichiers)
10fetch('https://api.jurassic-park.com/dinosaurs/42/image')
11 .then(response => response.blob())
12 .then(blob => {
13 const imageUrl = URL.createObjectURL(blob);
14 document.getElementById('dino-image').src = imageUrl;
15 });
16
17// Téléchargement et enregistrement d'un fichier (en environnement navigateur)
18fetch('https://api.jurassic-park.com/reports/monthly.pdf')
19 .then(response => response.blob())
20 .then(blob => {
21 const url = URL.createObjectURL(blob);
22 const a = document.createElement('a');
23 a.href = url;
24 a.download = 'monthly-report.pdf';
25 document.body.appendChild(a);
26 a.click();
27 document.body.removeChild(a);
28 URL.revokeObjectURL(url);
29 });Envoyer des formulaires
Fetch peut aussi gérer l'envoi de formulaires :
1// Envoi des données d'un formulaire en JSON
2document.getElementById('dino-form').addEventListener('submit', async function(e) {
3 e.preventDefault();
4
5 const form = e.target;
6 const formData = new FormData(form);
7
8 // Convertir FormData en un objet JavaScript
9 const formObject = {};
10 for (const [key, value] of formData.entries()) {
11 formObject[key] = value;
12 }
13
14 try {
15 const response = await fetch('https://api.jurassic-park.com/dinosaurs', {
16 method: 'POST',
17 headers: {
18 'Content-Type': 'application/json'
19 },
20 body: JSON.stringify(formObject)
21 });
22
23 if (!response.ok) {
24 throw new Error(`Erreur HTTP : ${response.status}`);
25 }
26
27 const result = await response.json();
28 alert(`Dinosaure ajouté avec succès ! ID : ${result.id}`);
29 form.reset();
30 } catch (error) {
31 console.error('Erreur lors de l\'envoi du formulaire :', error);
32 alert(`Erreur : ${error.message}`);
33 }
34});
35
36// Envoi d'un formulaire avec téléversement de fichiers
37document.getElementById('dino-upload-form').addEventListener('submit', async function(e) {
38 e.preventDefault();
39
40 const form = e.target;
41 const formData = new FormData(form);
42
43 try {
44 const response = await fetch('https://api.jurassic-park.com/dinosaurs/import', {
45 method: 'POST',
46 body: formData // FormData est automatiquement défini en 'multipart/form-data'
47 });
48
49 if (!response.ok) {
50 throw new Error(`Erreur HTTP : ${response.status}`);
51 }
52
53 const result = await response.json();
54 alert(`Import terminé ! ${result.count} dinosaures ajoutés.`);
55 } catch (error) {
56 console.error('Erreur pendant l\'import :', error);
57 alert(`Erreur : ${error.message}`);
58 }
59});CORS (Cross-Origin Resource Sharing)
CORS est un mécanisme de sécurité qui contrôle si une page peut effectuer des requêtes vers des ressources situées sur un autre domaine :
1// Requête CORS basique
2fetch('https://api.different-domain.com/data', {
3 mode: 'cors' // Valeur par défaut
4});
5
6// Requête avec authentification
7fetch('https://api.different-domain.com/protected-data', {
8 mode: 'cors',
9 credentials: 'include' // Envoyer les cookies cross-origin
10});Options pour credentials :
omit: Ne pas envoyer de cookies (par défaut)same-origin: Envoyer les cookies uniquement au même domaineinclude: Envoyer les cookies aux autres domaines aussi
Contrôle du cache
Fetch te permet de contrôler la façon dont le navigateur utilise son cache :
1// Ignorer le cache, toujours demander au serveur
2fetch(url, { cache: 'no-store' });
3
4// Vérifier d'abord le cache, puis demander si introuvable
5fetch(url, { cache: 'default' });
6
7// Vérifier d'abord le cache mais s'assurer auprès du serveur que la réponse est fraîche
8fetch(url, { cache: 'no-cache' });
9
10// Utiliser uniquement le cache, ne pas faire de requête réseau
11fetch(url, { cache: 'only-if-cached', mode: 'same-origin' });Patterns pratiques avec la Fetch API
Fonction universelle de requête API
1async function apiRequest(endpoint, options = {}) {
2 const apiBaseUrl = 'https://api.jurassic-park.com';
3 const url = `${apiBaseUrl}${endpoint}`;
4
5 const defaultOptions = {
6 headers: {
7 'Content-Type': 'application/json',
8 'Authorization': `Bearer ${localStorage.getItem('apiToken')}`
9 },
10 mode: 'cors'
11 };
12
13 const mergedOptions = {
14 ...defaultOptions,
15 ...options,
16 headers: {
17 ...defaultOptions.headers,
18 ...(options.headers || {})
19 }
20 };
21
22 if (mergedOptions.body && typeof mergedOptions.body === 'object' && !(mergedOptions.body instanceof FormData)) {
23 mergedOptions.body = JSON.stringify(mergedOptions.body);
24 }
25
26 try {
27 const response = await fetch(url, mergedOptions);
28
29 if (response.status === 401) {
30 await refreshAuthToken();
31 return apiRequest(endpoint, options);
32 }
33
34 if (response.status === 204) {
35 return { success: true };
36 }
37
38 if (!response.ok) {
39 try {
40 const errorData = await response.json();
41 throw new Error(errorData.message || `Erreur HTTP : ${response.status}`);
42 } catch (e) {
43 throw new Error(`Erreur HTTP : ${response.status}`);
44 }
45 }
46
47 const contentType = response.headers.get('content-type');
48 if (contentType && contentType.includes('application/json')) {
49 return await response.json();
50 } else if (contentType && contentType.includes('text/')) {
51 return await response.text();
52 } else {
53 return await response.blob();
54 }
55 } catch (error) {
56 console.error(`Erreur de requête API (${endpoint}) :`, error);
57
58 const apiError = new Error(error.message);
59 apiError.isNetworkError = !window.navigator.onLine;
60 apiError.endpoint = endpoint;
61 apiError.originalError = error;
62
63 throw apiError;
64 }
65}Système de cache simple pour les requêtes
1const apiCache = {
2 cache: new Map(),
3 ttl: 60000, // durée de vie du cache en ms (1 minute)
4
5 async get(url, options = {}) {
6 const cacheKey = `${url}|${JSON.stringify(options)}`;
7 const cachedItem = this.cache.get(cacheKey);
8
9 if (cachedItem && Date.now() < cachedItem.expiry) {
10 console.log(`Utilisation du cache pour : ${url}`);
11 return cachedItem.data;
12 }
13
14 console.log(`Requête en cours pour : ${url}`);
15 const response = await fetch(url, options);
16
17 if (!response.ok) {
18 throw new Error(`Erreur HTTP : ${response.status}`);
19 }
20
21 const data = await response.json();
22
23 if (!options.method || options.method === 'GET') {
24 this.cache.set(cacheKey, { data, expiry: Date.now() + this.ttl });
25 }
26
27 return data;
28 },
29
30 invalidate(urlPattern) {
31 for (const key of this.cache.keys()) {
32 if (key.includes(urlPattern)) {
33 this.cache.delete(key);
34 }
35 }
36 },
37
38 clear() { this.cache.clear(); }
39};Pattern Retry - nouvelles tentatives automatiques après échec
1/**
2 * Exécute une fonction avec des nouvelles tentatives automatiques
3 * @param {Function} fn - Fonction à exécuter qui retourne une Promise
4 * @param {Object} options - Options
5 * @param {number} options.maxRetries - Nombre maximum de tentatives
6 * @param {number} options.retryDelay - Délai avant nouvelle tentative (ms)
7 * @param {Function} options.shouldRetry - Fonction déterminant s'il faut réessayer
8 * @param {number} options.backoffFactor - Multiplicateur de délai (backoff)
9 * @returns {Promise} - Promise avec le résultat de la fonction ou la dernière erreur
10 */
11async function withRetry(fn, options = {}) {
12 const {
13 maxRetries = 3,
14 retryDelay = 1000,
15 shouldRetry = (error) => true,
16 backoffFactor = 2
17 } = options;
18
19 let lastError;
20
21 for (let attempt = 0; attempt <= maxRetries; attempt++) {
22 try {
23 return await fn();
24 } catch (error) {
25 lastError = error;
26
27 if (attempt === maxRetries || !shouldRetry(error)) {
28 break;
29 }
30
31 const delay = retryDelay * Math.pow(backoffFactor, attempt);
32 console.log(`Tentative ${attempt + 1} échouée. Nouvelle tentative dans ${delay}ms...`);
33 await new Promise(resolve => setTimeout(resolve, delay));
34 }
35 }
36
37 throw lastError;
38}
39
40// Exemple d'utilisation avec la Fetch API
41async function fetchFromUnstableAPI() {
42 return withRetry(
43 () => fetch('https://api.jurassic-park.com/unstable-endpoint')
44 .then(response => {
45 if (!response.ok) throw new Error(`Erreur HTTP : ${response.status}`);
46 return response.json();
47 }),
48 {
49 maxRetries: 5,
50 retryDelay: 500,
51 backoffFactor: 1.5,
52 shouldRetry: (error) => {
53 // Ne réessayer que pour les erreurs 5xx (erreurs serveur) ou les erreurs réseau
54 return error.message.includes('Erreur HTTP : 5') ||
55 error instanceof TypeError;
56 }
57 }
58 );
59}Limitations et compatibilité de la Fetch API
La Fetch API est disponible dans tous les navigateurs modernes, et depuis la version 18 elle est aussi disponible nativement dans Node.js. Cependant, il existe certaines limitations et problèmes de compatibilité :
Pas de suivi de progression des requêtes - Fetch n'a pas de suivi de progression intégré pour l'envoi/le téléchargement. Pour ces cas, XMLHttpRequest peut être une meilleure option.
Pas de timeout natif - Les timeouts doivent être implémentés à l'aide d'AbortController.
Ne rejette pas les Promises pour les erreurs HTTP - Même si le serveur retourne un code d'erreur, la Promise de Fetch sera résolue. Tu dois vérifier
response.ok.
Dans les anciens navigateurs, tu peux avoir besoin d'utiliser un polyfill comme whatwg-fetch ou une bibliothèque comme axios qui offre une API cohérente entre les environnements.
Alternatives à la Fetch API
Bien que la Fetch API soit disponible nativement, il existe des alternatives qui peuvent offrir des fonctionnalités supplémentaires :
Axios - Une bibliothèque populaire qui offre :
- Une API cohérente entre le navigateur et Node.js
- Une transformation automatique des données JSON
- Une protection XSRF intégrée
- Le suivi de progression des requêtes
- Des timeouts intégrés
- Des nouvelles tentatives automatiques
- L'annulation des requêtes
jQuery.ajax() - Dans les anciens projets utilisant jQuery
XMLHttpRequest - Une alternative plus ancienne et plus verbeuse qui offre plus de contrôle, par ex. sur la progression des requêtes
Quand utiliser la Fetch API plutôt que les alternatives ?
Fetch est idéal quand :
- Tu démarres un nouveau projet
- Tu veux minimiser les dépendances
- Tu as besoin de requêtes HTTP simples
- Tu travailles avec des navigateurs modernes
Envisage les alternatives quand :
- Tu as besoin de fonctionnalités plus avancées (progression, retries intégrés)
- Tu dois supporter d'anciens navigateurs sans polyfills
- Tu travailles dans un environnement où Fetch ne fonctionne pas bien
- Tu utilises déjà une bibliothèque qui offre des fonctionnalités HTTP
Bonnes pratiques de la Fetch API
Toujours vérifier response.ok
1fetch(url).then(response => { 2 if (!response.ok) throw new Error(`Erreur HTTP : ${response.status}`); 3 return response.json(); 4});Définir des en-têtes appropriés
1fetch(url, { 2 headers: { 3 'Content-Type': 'application/json', 4 'Accept': 'application/json' 5 } 6});Utiliser AbortController pour les timeouts
1const controller = new AbortController(); 2const timeout = setTimeout(() => controller.abort(), 5000); 3 4fetch(url, { signal: controller.signal }) 5 .then(response => /* ... */) 6 .catch(error => /* ... */) 7 .finally(() => clearTimeout(timeout));Fais attention au traitement du corps de la réponse
1// INCORRECT - tenter d'utiliser le corps plusieurs fois 2fetch(url) 3 .then(response => { 4 const text = response.text(); // Cela consomme le corps ! 5 const json = response.json(); // Erreur - corps déjà consommé ! 6 return /* ... */; 7 }); 8 9// CORRECT - cloner la réponse si tu dois l'utiliser plusieurs fois 10fetch(url) 11 .then(response => { 12 const clone = response.clone(); 13 return response.text().then(text => { 14 return clone.json().then(json => { 15 return { text, json }; 16 }); 17 }); 18 });Utilise async/await pour la lisibilité
1async function fetchData() { 2 try { 3 const response = await fetch(url); 4 if (!response.ok) throw new Error(`Erreur HTTP : ${response.status}`); 5 const data = await response.json(); 6 return data; 7 } catch (error) { 8 console.error('Erreur :', error); 9 throw error; 10 } 11}
Résumé
La Fetch API est un outil puissant et intégré pour effectuer des requêtes HTTP en JavaScript. Ses principaux avantages sont son API basée sur les Promises, sa facilité d'utilisation et sa polyvalence. Bien qu'elle présente certaines limitations par rapport à des bibliothèques plus avancées comme Axios, pour la plupart des cas d'usage elle est tout à fait suffisante.
Au Parc Jurassique, où une communication fiable entre les différents systèmes est essentielle à la sécurité des visiteurs et des dinosaures, la Fetch API permet un échange de données fiable entre les systèmes de surveillance, les bases de données et les interfaces utilisateur.
Dans le prochain exercice, nous verrons comment combiner la Fetch API avec d'autres techniques asynchrones pour créer des systèmes encore plus avancés de gestion de notre Parc Jurassique numérique.
Code de cette leçon : index.js
1// Callback Function Builder - Fonctions de rappel
2console.log("CALLBACK FUNCTIONS - PARK EVENT SYSTEM\n");
3
4// Callback - fonction passée en argument
5function monitorDinosaur(species, callback) {
6 console.log(`Je démarre le monitoring: ${species}`);
7
8 // Simulation du monitoring (synchrone pour simplifier)
9 const status = Math.random() > 0.5 ? 'SAFE' : 'ALERT';
10 const data = {
11 species: species,
12 status: status,
13 timestamp: new Date().toLocaleTimeString()
14 };
15
16 // Calling callback with results
17 callback(data);
18}
19
20// Example 1: Simple callback
21console.log("=== EXAMPLE 1: CALLBACK SIMPLE ===\n");
22
23monitorDinosaur('T-Rex', function(result) {
24 console.log(`Résultat du monitoring:`);
25 console.log(` Espèce: ${result.species}`);
26 console.log(` Status: ${result.status}`);
27 console.log(` Heure: ${result.timestamp}`);
28});
29
30// Example 2: Arrow function comme callback
31console.log("\n=== EXAMPLE 2: ARROW FUNCTION ===\n");
32
33monitorDinosaur('Velociraptor', (result) => {
34 const emoji = result.status === 'SAFE' ? 'OK' : '!';
35 console.log(`${emoji} ${result.species}: ${result.status} (${result.timestamp})`);
36});
37
38// Example 3: Different callbacks pour different situations
39console.log("\n=== EXAMPLE 3: CONDITIONAL CALLBACKS ===\n");
40
41function checkDinosaurWithCallbacks(species, onSafe, onAlert) {
42 const isSafe = Math.random() > 0.5;
43
44 if (isSafe) {
45 onSafe(species);
46 } else {
47 onAlert(species);
48 }
49}
50
51checkDinosaurWithCallbacks(
52 'Triceratops',
53 (species) => console.log(`${species} est en sécurité`),
54 (species) => console.log(`ALERT! Problème avec ${species}!`)
55);
56
57// Example 4: Callback avec array methods
58console.log("\n=== EXAMPLE 4: ARRAY METHODS WITH CALLBACKS ===\n");
59
60const dinosaurs = [
61 { name: 'T-Rex', danger: 10 },
62 { name: 'Velociraptor', danger: 8 },
63 { name: 'Triceratops', danger: 3 },
64 { name: 'Brachiosaurus', danger: 2 }
65];
66
67console.log("forEach - itération:");
68dinosaurs.forEach((dino) => {
69 console.log(` - ${dino.name} (danger: ${dino.danger})`);
70});
71
72console.log("\nfilter - dangerous uniquement (>5):");
73const dangerous = dinosaurs.filter((dino) => dino.danger > 5);
74dangerous.forEach(dino => console.log(` ${dino.name}`));
75
76console.log("\nmap - noms:");
77const names = dinosaurs.map((dino) => dino.name);
78console.log(` ${names.join(', ')}`);
79
80console.log("\nreduce - total danger:");
81const totalDanger = dinosaurs.reduce((sum, dino) => sum + dino.danger, 0);
82console.log(` Total: ${totalDanger}`);
83
84// Example 5: Callback hell (problem!)
85console.log("\n=== EXAMPLE 5: CALLBACK HELL ===\n");
86
87function step1(callback) {
88 console.log("Étape 1: Vérification de fence...");
89 callback();
90}
91
92function step2(callback) {
93 console.log("Étape 2: Vérification de dinosaurs...");
94 callback();
95}
96
97function step3(callback) {
98 console.log("Étape 3: Final report...");
99 callback();
100}
101
102// Nested callbacks (callback hell)
103step1(() => {
104 step2(() => {
105 step3(() => {
106 console.log("Toutes les étapes terminées");
107 });
108 });
109});
110
111console.log("\nProblem: Callback hell - difficile à lire !");
112console.log("Solution: Promises or async/await (in the next lessons)");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. Que retourne la fonction fetch() en JavaScript ?
2. À quoi sert AbortController dans le contexte de la Fetch API ?
Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Éditeur de code
Écris une fonction async qui récupère des données depuis une API avec fetch(), parse la réponse JSON et affiche une liste de dinosaures.
- Clique dans l'ordre
Ordonne les éléments d'une chaîne de Promises :
- Éditeur de code
Écris du code utilisant Promise.allSettled pour vérifier plusieurs capteurs en parallèle. Analyse les résultats - lesquels ont réussi (fulfilled) et lesquels ont échoué (rejected).
- Tri vertical
Ordonne les éléments de la gestion d'erreurs en async/await :