Avancé30 min de lecture

Promises

Comprends le JavaScript asynchrone avec les Promises — création, chaînage, et gestion des états de succès et d'échec.

Pourquoi l'asynchrone ?

JavaScript est mono-thread — il ne peut exécuter qu'un seul morceau de code à la fois. Si une opération longue (comme une requête réseau ou un timer) s'exécutait de manière synchrone, toute la page se figerait jusqu'à ce qu'elle se termine.

La programmation asynchrone résout ce problème en permettant au code de continuer à s'exécuter pendant qu'on attend que les opérations lentes se terminent. Le résultat est délivré plus tard, quand il est prêt.

Les callbacks étaient la solution originale :

javascript
setTimeout(function() {
  console.log('Done after 1 second');
}, 1000);
console.log('This runs immediately');

Mais les callbacks mènent rapidement à du code profondément imbriqué et difficile à lire, connu sous le nom de "callback hell" :

javascript
getUser(id, function(user) {
  getOrders(user, function(orders) {
    getDetails(orders[0], function(details) {
      // 3 niveaux de profondeur et ça continue...
    });
  });
});

Les Promises ont été introduites dans ES6 pour résoudre ce problème en fournissant une API plus propre et chaînable pour les opérations asynchrones.

Les bases des Promises

Une Promise est un objet qui représente une valeur qui peut ne pas être disponible maintenant mais le sera à un moment donné dans le futur (ou échouera).

Une Promise a trois états possibles :

  • Pending — l'état initial ; l'opération n'est pas encore terminée.
  • Fulfilled — l'opération s'est terminée avec succès et a produit une valeur.
  • Rejected — l'opération a échoué et a produit une raison (une erreur).

Une fois qu'une Promise est fulfilled ou rejected, elle est settled et son état ne peut plus changer.

Créer une Promise :

javascript
const myPromise = new Promise((resolve, reject) => {
  // Do some async work...
  const success = true;
  if (success) {
    resolve('It worked!');  // Fulfill the promise
  } else {
    reject('Something failed');  // Reject the promise
  }
});

Consommer une Promise :

javascript
myPromise
  .then(value => console.log(value))   // 'It worked!'
  .catch(error => console.log(error))  // handles rejection
  .finally(() => console.log('Done')); // runs either way
  • .then(onFulfilled) — appelée quand la Promise est fulfilled.
  • .catch(onRejected) — appelée quand la Promise est rejected.
  • .finally(onSettled) — appelée quand la Promise est settled (fulfilled ou rejected), utile pour le nettoyage.

Chaînage de Promises

L'une des fonctionnalités les plus puissantes des Promises est le chaînage. Chaque appel à .then() retourne une nouvelle Promise, te permettant de chaîner plusieurs étapes asynchrones en séquence.

javascript
fetch('/api/user')
  .then(response => response.json())   // Parse JSON
  .then(user => fetch('/api/orders/' + user.id)) // Fetch orders
  .then(response => response.json())   // Parse JSON
  .then(orders => console.log(orders)) // Use the data
  .catch(error => console.error(error)); // Handle ANY error

Règles clés du chaînage :

  1. Retourner une valeur depuis .then() l'enveloppe automatiquement dans une Promise résolue :
javascript
Promise.resolve(1)
  .then(n => n + 1)   // Returns 2, wrapped in Promise.resolve(2)
  .then(n => n * 3)   // Returns 6
  .then(n => console.log(n)); // 6
  1. Retourner une Promise depuis .then() est attendu avant que le prochain .then() ne s'exécute.

  2. Les erreurs se propagent le long de la chaîne jusqu'au .catch() le plus proche :

javascript
Promise.resolve('start')
  .then(() => { throw new Error('Oops'); })
  .then(() => console.log('Skipped'))  // Skipped!
  .catch(err => console.log(err.message)); // 'Oops'

Cette chaîne plate et lisible remplace complètement le pattern de callbacks imbriqués.

Utilitaires Promise

JavaScript fournit plusieurs méthodes statiques sur le constructeur Promise pour travailler avec plusieurs Promises à la fois.

Promise.all(iterable) — attend que toutes les Promises soient fulfilled. Si l'une est rejected, tout le résultat est rejected :

javascript
Promise.all([fetchUser(), fetchOrders(), fetchSettings()])
  .then(([user, orders, settings]) => { /* all done */ })
  .catch(err => { /* one failed */ });

Promise.race(iterable) — se résout ou est rejetée avec la première Promise à être settled :

javascript
Promise.race([fetchData(), timeout(5000)])
  .then(data => console.log(data))
  .catch(() => console.log('Timed out'));

Promise.allSettled(iterable) — attend que toutes les Promises soient settled (pas de court-circuit en cas de rejection). Chaque résultat a { status, value } ou { status, reason } :

javascript
Promise.allSettled([p1, p2, p3])
  .then(results => {
    results.forEach(r => console.log(r.status)); // 'fulfilled' or 'rejected'
  });

Promise.any(iterable) — se résout avec la première Promise à être fulfilled. N'est rejetée que si toutes sont rejected (avec une AggregateError) :

javascript
Promise.any([fetchFromCDN1(), fetchFromCDN2()])
  .then(data => console.log('Got data from fastest CDN'));

Promise en action

html
<div id="output"></div>

<script>
  // Simulate an async operation with setTimeout + Promise
  function fetchUser(id) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (id > 0) {
          resolve({ id: id, name: 'Alice' });
        } else {
          reject('Invalid user ID');
        }
      }, 500);
    });
  }

  const output = document.getElementById('output');
  output.innerHTML = '<p>Loading...</p>';

  fetchUser(1)
    .then(user => {
      output.innerHTML = `<p>User: ${user.name} (ID: ${user.id})</p>`;
      return user.name.toUpperCase();
    })
    .then(upperName => {
      output.innerHTML += `<p>Uppercase: ${upperName}</p>`;
    })
    .catch(error => {
      output.innerHTML = `<p>Error: ${error}</p>`;
    })
    .finally(() => {
      output.innerHTML += '<p>Request complete.</p>';
    });
</script>

Quels sont les trois états d'une Promise ?

Prêt à pratiquer ?

Crée ton compte gratuit pour accéder à l'éditeur de code interactif, lancer les défis et suivre ta progression.