Cours JavaScript et TypeScript · Module 4: JavaScript asynchrone

Fetch API

17 min de lecture
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éthodeDescription
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 domaine
  • include : 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é :

  1. 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.

  2. Pas de timeout natif - Les timeouts doivent être implémentés à l'aide d'AbortController.

  3. 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 :

  1. 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
  2. jQuery.ajax() - Dans les anciens projets utilisant jQuery

  3. 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

  1. 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});
  2. Définir des en-têtes appropriés

    1fetch(url, {
    2  headers: {
    3    'Content-Type': 'application/json',
    4    'Accept': 'application/json'
    5  }
    6});
  3. 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));
  4. 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  });
  5. 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. 1. Que retourne la fonction fetch() en JavaScript ?

  2. 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 :

Articles utiles