Promise JavaScript

Avant d'apprendre ce chapitre, vous devez comprendre ce qu'est la programmation asynchrone. Vous pouvez vous référer à :Programmation asynchrone en JavaScript

Promise est une classe fournie par ECMAScript 6, conçue pour écrire des tâches asynchrones complexes de manière plus élégante.

Promise est un objet en JavaScript utilisé pour gérer des opérations asynchrones. Il représente l'achèvement final (ou l'échec) d'une opération asynchrone et sa valeur résultante.

En termes simples, une Promise est un « engagement » selon lequel un résultat sera renvoyé à un moment donné dans le futur (soit un résultat réussi, soit une raison d'échec).

Une Promise a trois états :

  • pending : état initial, ni fulfilled ni rejected
  • fulfilled : signifie que l'opération s'est terminée avec succès
  • rejected : signifie que l'opération a échoué

Exemple

const myPromise = new Promise((resolve, reject) => {
  // code d'opération asynchrone
 
  if (/* Opération réussie */) {
    resolve('Résultat de succès'); // Changer l'état de la Promise en fulfilled
  } else {
    reject('Raison de l'échec'); // Changer l'état de la Promise en rejected
  }
});

Prise en charge des navigateurs

Promise étant une nouveauté d'ES6, certains navigateurs plus anciens ne le prennent pas en charge. Apple Safari 10 et Windows Edge 14 et versions ultérieures ont commencé à prendre en charge les fonctionnalités d'ES6.

Ce qui suit montre la prise en charge de Promise par les navigateurs :

Chrome 58 Edge 14 Firefox 54 Safari 10 Opera 55

Comment utiliser Promise

Méthode then()

then()Cette méthode est utilisée pour spécifier des fonctions de rappel lorsque l'état de la Promise passe à accompli ou rejeté.

Exemple

myPromise.then(
  (result) => {
    // Gérer le cas de succès
    console.log('Succès:', result);
  },
  (error) => {
    // Gérer le cas d'échec
    console.error('Échec:', error);
  }
);

Méthode catch()

catch()Cette méthode est spécifiquement utilisée pour gérer les cas où la Promise est rejetée.

Exemple

myPromise
  .then((result) => {
    console.log('Succès:', result);
  })
  .catch((error) => {
    console.error('Échec:', error);
  });

Méthode finally()

finally()Cette méthode s'exécute indépendamment de l'état final de la Promise.

Exemple

myPromise
  .then((result) => {
    console.log('Succès:', result);
  })
  .catch((error) => {
    console.error('Échec:', error);
  })
  .finally(() => {
    console.log('Opération terminée');
  });

Chaînage de promesses

Une fonctionnalité puissante de Promise est que plusieurs opérations asynchrones peuvent être enchaînées.

Exemple

doFirstThing()
  .then((result) => doSecondThing(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => {
    console.log('Résultat final :', finalResult);
  })
  .catch((error) => {
    console.error('Erreur quelque part dans la chaîne :', error);
  });

Méthodes statiques de Promise

Promise.all()

Attendre que toutes les promesses se terminent, ou qu'une seule promesse échoue.

Exemple

Promise.all([promise1, promise2, promise3])
  .then((results) => {
    // results est un tableau contenant tous les résultats des promesses
    console.log(results);
  })
  .catch((error) => {
    // On entre ici si une promesse échoue
    console.error(error);
  });

Promise.race()

Renvoie le résultat de la première promesse à se terminer (que ce soit un succès ou un échec).

Exemple

Promise.race([promise1, promise2, promise3])
  .then((result) => {
    // Utiliser le résultat de la première promesse terminée
    console.log(result);
  })
  .catch((error) => {
    // Si la première promesse terminée est rejetée
    console.error(error);
  });

Promise.resolve() et Promise.reject()

Créez rapidement une Promise résolue ou rejetée.

Exemple

const resolvedPromise = Promise.resolve('Immediately resolved value');
const rejectedPromise = Promise.reject('Immediately rejected reason');

Exemples d'applications pratiques

Exemple 1: Utilisation de Promise pour gérer les requêtes AJAX

Exemple

function fetchData(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.response);
      } else {
        reject(new Error(xhr.statusText));
      }
    };
    xhr.onerror = () => reject(new Error('Erreur réseau'));
    xhr.send();
  });
}

fetchData('https://api.example.com/data')
  .then((data) => {
    console.log('Données récupérées avec succès :', data);
  })
  .catch((error) => {
    console.error('Échec de la récupération des données :', error);
  });

Exemple 2 : Utilisation d'une chaîne de promesses pour gérer plusieurs opérations asynchrones

Exemple

function getUser(userId) {
  return fetch(`/users/${userId}`);
}

function getPosts(userId) {
  return fetch(`/users/${userId}/posts`);
}

getUser(123)
  .then((user) => {
    console.log('Récupération des informations utilisateur :', user);
    return getPosts(user.id);
  })
  .then((posts) => {
    console.log('Récupération des publications de l'utilisateur :', posts);
  })
  .catch((error) => {
    console.error('Échec de l'opération :', error);
  });

Problèmes courants et bonnes pratiques

1. Éviter l'imbrication de promesses

Mauvaise approche (la version Promise de l'enfer des callbacks) :

Exemple

doFirstThing().then((firstResult) => {
  doSecondThing(firstResult).then((secondResult) => {
    doThirdThing(secondResult).then((thirdResult) => {
      console.log(thirdResult);
    });
  });
});

Approche correcte (utiliser le chaînage) :

Exemple

doFirstThing()
  .then(doSecondThing)
  .then(doThirdThing)
  .then((finalResult) => {
    console.log(finalResult);
  });

2. Toujours gérer les rejets

Oublier de gérer les rejets de Promise provoquera des erreurs "uncaught promise rejection". Utilisez toujours.catch()outry/catch(dans async/await) pour gérer les erreurs.

3. Les promesses ne sont pas annulables

Une fois créée, une promesse ne peut pas être annulée. Si vous avez besoin d'une fonctionnalité d'annulation, envisagez d'utiliser AbortController ou d'autres modèles.

4. FAQ

Q : Peut-on inverser l'ordre de then, catch et finally ?

R : Oui, l'effet est exactement le même. Mais ce n'est pas recommandé ; il est préférable d'écrire le programme dans l'ordre then-catch-finally.

Q : Outre le bloc then, les deux autres blocs peuvent-ils être utilisés plusieurs fois ?

R : Oui. finally, comme then, s'exécute dans l'ordre, mais seul le premier bloc catch s'exécutera, sauf s'il y a une exception dans le bloc catch. Il est donc préférable de ne prévoir qu'un seul bloc catch et un seul bloc finally.

Q : Comment interrompre un bloc then ?

R : Un bloc then s'exécute séquentiellement vers le bas par défaut ; return ne peut pas l'interrompre. Vous pouvez utiliser throw pour sauter vers catch et réaliser l'interruption.

Q : Quand est-il approprié d'utiliser Promise plutôt que les fonctions de rappel traditionnelles ?

R : Lorsque plusieurs opérations asynchrones doivent être exécutées séquentiellement. Par exemple, si vous souhaitez vérifier un nom d'utilisateur et un mot de passe séquentiellement via des méthodes asynchrones, vous devez d'abord vérifier le nom d'utilisateur de manière asynchrone, puis vérifier le mot de passe de manière asynchrone — c'est une situation bien adaptée à Promise.

Q : Est-ce que Promise est une méthode pour convertir l'asynchrone en synchrone ?

R : Pas du tout. Promise n'est qu'un meilleur style de programmation.

Q : Quand devons-nous écrire un autre then au lieu de continuer à programmer dans le then actuel ?

R : Lorsque vous devez appeler une autre tâche asynchrone.


Promise et async/await

ES2017 a introduit async/await, qui est basé sur Promise et fournit une syntaxe plus intuitive pour gérer les opérations asynchrones.

Exemple

async function fetchData() {
  try {
    const user = await getUser(123);
    const posts = await getPosts(user.id);
    console.log('Publications de l’utilisateur :', posts);
  } catch (error) {
    console.error('Échec de la récupération des données :', error);
  }
}

Bien que async/await soit plus lisible, comprendre les concepts de base de Promise reste très important, car c'est la base d'async/await.

Autres extensions