Apprendre Javascript pour rendre vos pages Web interactives

Auteur: Mohamed CHINY Durée necessaire pour le cours de Apprendre Javascript pour rendre vos pages Web interactives Niveau recommandé pour le cours de Apprendre Javascript pour rendre vos pages Web interactives Supports vidéo disponibles pour ce cours Exercices de renforcement disponibles pour ce cours Quiz disponible pour ce cours

Page 33: Les promesses - Objet Promise

Toutes les pages

Les promesses (ou objet Promise)

Les promesses et le traitement asynchrone en Javascript

Si vous demandez aux programmeurs Javascript, quelle est la meilleure chose que l’ECMAScript 2015 (ou ES6) a apporté, ils vous répondront certainement d’une seule voix : Les promesse.

En effet, Les promesses constituent une révolution en ce qui concerne la manière avec laquelle le moteur Javascript exécute le code, en tirant profit de sa fameuse boucle d’événement qui permet d’agencer l’exécution des fonctions, d’autant plus pour exécuter des tâches asynchrones qui nécessitent l’attente des résultats avant d’exécuter une tâche spécifique tout en donnant la main au reste du code de s’exécuter, ce qui donne au Javascript son caractère non bloquant vis-à-vis des tâches qui dépendent de tiers, comme un serveur ou une API.

Comme son nom l’indique, une promesse est un objet Javascript qui renvoie, non pas une valeur à l’instant même, mais une promesse de cette valeur-là, et c’est au moment où cette dernière est disponible qu’elle sera transmise au code pour poursuivre son exécution.

A titre d’exemple, imaginons que vous voulez interroger une base de données afin d’en extraire des informations nécessaires à l’exécution d’un traitement. Comme vous l’aurez deviné, ces données-là mettront un certain temps pour qu’elles parviennent au script. Cependant, seulement la partie du code qui dépend de ces données-là sera mise en attente, alors que le reste du code poursuivra son exécution normalement.

Un traitement pareil n’est pas nouveau en Javascript. En effet, on faisait déjà cela à l’aide du fameux objet XMLHttpRequest invoqué en AJAX, mais bien que le concept ne soit pas nouveau, son implémentation à l’aide des promesses constitue une grande avancée en terme de la programmation asynchrone.

Objet Promise

L'objet Promise permet de réaliser des traitements asynchrones. Pour créer une promesse, on fait appel au constructeur Promise() comme ceci:
p=new Promise(()=>{
});
Comme argument, le constructeur Promise() accueille une fonction. Dans l'exemple ci-dessus, j'ai opté pour une fonction fléchée (arrow fonction) vue son écriture simple et claire.

Si on tente d'afficher l'objet p dans la console on aura ceci:
Promise { <state>: "pending" }
Le navigateur indique clairement que c'est une promesse avec l'état (state) "pending". En effet, une promesse peut avoir trois états:
  • pending: (ou en attente) signifie que la promesse est en cours et aucune réponse n'a encore été reçue par le navigateur.
  • fulfilled: (ou remplie) signifie que la promesse a été respectée (ou honorée) et que les données (promises) sont disponibles pour le traitement.
  • rejected: (ou rejetée) signifie que la promesse a été rejetée, c'est à dire que les données promises ne sont pas reçues.

En plus de l'état (ou state), une promesse remplie dispose de la propriété valeur (value) et une promesse rejetée dispose de la propriété raison (reason).

La fonction passée en argument du constructeur Promise() accueille également deux fonctions de rappel (callback function). La première fonction sera appelée si la promesse réussit et la deuxième sera appelée quand la promesse échoue.
Les fonctions de callback passées en argument sont souvent nommées resolve et reject. Cependant, vous pouvez les nommez comme bon vous semble. Il suffit de prendre en compte que la première fonction correspond à une promesse respectée et la deuxième à une promesse qui a échoué.

Prenons cet exemple:
p=new Promise((resolve,reject)=>{
   nbr=Math.round(Math.random()*10);
   if(nbr%2==0)
      resolve("Pair");
   else
      reject("Impair");
});

console.log(p);
Dans ce cas, on imagine que l'on souhaite générer un nombre aléatoire pair. Si c'est le cas, on considère que la promesse d'un nombre paire appelle la fonction resolve(), alors qu'un nombre paire va appeler la fonction reject.

Si on exécute le code, alors on aura dans la console quelque chose qui ressemble à ceci si le nombre est pair:
Promise { <state>: "fulfilled", <value>: "Pair" }
Si le nombre est impair alors on aura cela:
Promise { <state>: "rejected", <reason>: "Impair" }
Dans les ceux cas, la valeur et la raison correspondent respectivement aux valeurs des arguments passés aux fonctions resolve() et reject().

Mots-clés then et catch

Pour attacher un traitement à une promesse on fait appel aux mots-clés then et catch. Ils s'agit en fait de méthodes de l'objet Promise. La méthode then() s'exécute quand la promesse est respectée, autrement dit, si l'état de la promesse est fulfilled. Par contre, la méthode catch() s'exécute quand la promesse échoue ce qui signifie que l'état de cette dernière est rejected.

Les méthodes then() et catch() sont aussi des promesses et elles acceptent une fonction de callback en guise d'argument. Cette même fonction possède comme argument la valeur de l'argument de la fonction resolve() en cas de fulfilled ou celle de la fonction reject() en cas de rejected.

Regardons cet exemple:
p=new Promise((resolve,reject)=>{
   nbr=Math.round(Math.random()*10);
   if(nbr%2==0)
      resolve("Pair");
   else
      reject("Impair");
});

p.then((val)=>{
   console.log(val);
   // Affiche "Pair" dans le cas de l'état fulfilled
}).catch((val)=>{
   console.log(val);
   // Affiche "Impair" dans le cas de l'état rejected
})
Remarquez que la méthode catch() est chainée à la méthode then().

On peut abréger cette écriture de cette façon:
new Promise((resolve,reject)=>{
   nbr=Math.round(Math.random()*10);
   if(nbr%2==0)
      resolve("Pair");
   else
      reject("Impair");
}).then((val)=>{
   console.log(val);
   // Affiche Pair dans le cas de l'état fulfilled
}).catch((val)=>{
   console.log(val);
   // Affiche Impair dans le cas de l'état rejected
})
Dans ce cas, même pas la peine de déclarer l'objet p et on chaine directement then() à l'instance du constructeur Promise().

Chaînage de promesses

Il est possible d'enchainer les mots-clés then() et catch() les uns aux autres autant de fois que l'on souhaite. Pratiquement, cela sert à effectuer un traitement après qu'un traitement aura fini ou échoué. Notez que then() et catch() sont aussi des promesses, donc les méthodes then() et catch() qui leurs seront chaînées s'exécuteront dans le mode asynchrone également, ce qui est très pratique dans certains cas comme le fait de se connecter à un serveur de base de données, puis exécuter une requête SQL, puis récupérer les données pour les traiter en Javascript. Chacune de ces opérations pourrait prendre un moment et le traitement suivant ne pourra être exécuté que si le traitement précédent aura fini.

Pour enchaîner les promesses, il faut que les méthodes then() ou catch() précédentes retournent une valeur. Cette valeur constituera l'argument de la fonction du callback de la méthode then() ou catch() suivante.

Voici une exemple:
new Promise((resolve,reject)=>{
   nbr=Math.round(Math.random()*10);
   if(nbr%2==0)
      resolve("Pair");
   else
      reject("Impair");
}).then((val)=>{
   console.log(val);
    // Affiche "Pair" dans le cas de l'état fulfilled
   return "Il s'agit bien d'un nombre pair";
}).then((val)=>{
   console.log(val);
   // Affiche "Il s'agit bien d'un nombre pair" en cas de fulfilled
}).catch((val)=>{
   console.log(val);
   // Affiche "Impair" dans le cas de l'état rejected
   return "Il s'agit là d'un nombre impair";
}).then((val)=>{
   console.log(val);
   // Affiche "Il s'agit là d'un nombre impair" en cas de rejected
})

Les promesses - Objet Promise en vidéo