1NSI : Les Promesses en JS⚓︎
Introduction⚓︎
En Javascript, dans l'ancien temps (pour les versions ES5 / ES2009, jusqu'en 2015), pour réaliser des requêtes asynchrones de données via le réseau, on utilisait des Callbacks.
Les Callbacks sont des fonctions JS qui n'avaient réellement de sens, que lorsqu'elles étaient déclenchées après qu'un certain événement se soit produit, comme par ex. après un clic de souris. Bien que beaucoup plus illisible dans ce cas, elles étaient également utilisées dans le cas où il fallait appeler une (autre) fonction APRÈS avoir fini d'exécuter une fonction.
Il est fréquent de souhaiter tirer parti des fonctionnalités de programmation asynchrone de Javascript, plus précisément, il est fréquent de faire des requêtes de données asynchrones vers des API (interfaces de programmation), externes (sites tiers qui vous offrent des services de livraison de données - gratuitement ou pas) ou internes (votre propre site, votre base de données, etc..). Ces données mettent donc un certain temps à arriver vers votre code javascript, et il est fréquent de souhaiter d'avoir la certitude que ces données sont bien arrivées dans votre code, AVANT de poursuivre vers d'autres tâches, par exemple une nouvelle requête asynchrone, etc..
Avec l'ancienne méthode, en utilisant les callbacks, on se retrouvait ainsi rapidement enfermé dans ce que l'on appelle l'enfer des callbacks (code illisible, et non maintainable).
Pour résoudre cela, on a inventé les Promesses en Javascript. Intuitivement, on pourra penser à une promesse de envoi/réception de données depuis un serveur (externe - avec une certaine API-, ou interne -depuis mon serveur perso-). Ou plus généralement, à la promesse d'un certain traitement à effectuer.
Les Promesses JS existent depuis ES2015.
- Pourquoi on devrait les utiliser ?
- Qu'est-ce qu'une promesse ?
- Comment les utiliser ?
Promesses⚓︎
Fonctions renvoyant une Promesse⚓︎
Les fonctions synchrones ("normales") en javascript renvoient des résultats.
Par opposition, les fonctions asynchrones renvoient des promesses (qui sont des objets, au sens du javascript).
Pas toutes les fonctions javascript ne renvoient (ou ne peuvent renvoyer) des promesses. Il faut lire la documentation javascript, pour savoir si telle ou telle fonction renvoie une promesse (asynchrone) ou un résultat (synchrone).
Il est possible également de créer/programmer nos propres fonctions asynchrones qui renvoient une promesse (avec la syntaxe new Promise
que nous verrons plus tard). Pour le moment, voyons comment utiliser la fonction fetch
(interne au javascript) qui demande des données sur un serveur situé à une certaine url
:
de Syntaxe de la fonction fetch
La fonction fetch
est une fonction interne au javascript, qui renvoie une promesse (de renvoi de données), dont voici une syntaxe possible :
let promesse = fetch(url)
Mais le principe d'une promesse est d'être un objet qui se trouve dans un certain état, parmi les suivants :
pending
: dans l'attente de recevoir les donnéesfulfilled
: la requête a bien été résolue par le serveur (celui à qui on a demandé les données), ET j'ai bien reçu les donnéesrejected
: une erreur s'est produite
mot-clé .then()
: APRÈS la résolution de la promesse⚓︎
Le mot-clé .then(fonctionAExecuterApresReception)
permet au javascript de gérer en interne le suivi de ma requête asynchrone (je n'ai pas besoin de régulièrement demander dans quel état de résolution elle se trouve, c'est alors le javascript qui s'en charge).
La fonction fonctionAExecuterApresReception
donnée en paramètre à l'intérieur (entre les parentèses) de .then()
sera exécutée/appelée APRÈS QUE la requête sera fulfilled
(un peu comme une callback..). En résumé :
- on demande une promesse
PUIS /.then()
fonctionAExecuterApresReception
(si/lorsque la promesse est dans l'étatfulfilled
)
mot-clé .catch()
: EN CAS D'ERREUR de la résolution de la promesse⚓︎
Le mot-clé .catch(fonctionAExecuterEnCasDErreur)
permet au javascript de détecter en interne une éventuelle erreur lors du suivi de ma requête asynchrone (je n'ai pas besoin de régulièrement demander dans quel état de résolution elle se trouve, c'est alors le javascript qui s'en charge).
La fonction fonctionAExecuterEnCasDErreur
donnée en paramètre à l'intérieur (entre les parentèses) de .catch()
sera exécutée/appelée EN CAS D'ERREUR de la requête, si jamais elle arrive dans l'état rejected
(un peu comme une callback..). En résumé :
- on demande une promesse
PUIS / on exécute la suite du code (si/lorsque la promesse est dans l'état.then()
fulfilled
)- ou bien,
ON ATTRAPE/LÈVE (DÉTECTE ET GÈRE) UNE ERREUR /.catch()
fonctionAExecuterEnCasDErreur(si/lorsque la promesse est dans l'état
rejected`)
Un exemple de requête asynchrone⚓︎
pour voir Promise
(dans l'état pending
)⚓︎
1 2 3 4 |
|
pour voir Response
(dans l'état fulfilled
)⚓︎
Les données ont été reçues :
1 2 3 4 5 6 7 8 |
|
Usuellement, on ne prend même pas la peine de nommer les fonctions, et on préfère utiliser séquentiellement/chaîner des fonctions anonymes/non nommées, avec les notations/convention des fonctions fléchées. Le code qui suit est équivalent au code précédent :
1 2 3 4 5 |
|
Et s'il y avait une erreur .catch()
⚓︎
Pour simuler une erreur, vous pouvez (ATTENTION) modifier l'URL : par ex. ajouter/supprimer un caractère dans l'URL .. (ATTENTION : savoir revenir à l'URL correcte..)
1 2 3 4 5 6 |
|
Vous devriez recevoir l'erreur : TypeError: fetch failed
Données au format JSON⚓︎
Remarquons tout d'abord, qu'en fait, ce que nous avons noté data
dans le code ci-dessus, ne sont PAS les données reçues à proprement parler, mais plutôt la réponse à la promesse : c'est pourquoi nous la noterons désormais reponse
(au lieu de data
).
Ensuite, pour transformer la réponse reponse
de la promesse, au format de données json, que nous noterons donnesJson
: on peut faire cela en chaînant la fonction json()
(qui est une fonction interne au javascript) à la réponse response
.
Notons également que la fonction json()
renvoie elle-aussi une promesse... D'où le code :
1 2 3 4 5 6 7 |
|
Warning
- ATTENTION : Pour chaîner plusieurs
.then()
l'un derrière l'autre, il FAUT OBLIGATOIREMENT que CHAQUE.then()
renvoie en return les données (ou les promesses de données) pour l'étape d'après, éventuellement modifiées selon notre convenance. Dans notre exemple, nous avons choisi de renvoyerdonneesJson.word
(plutôt que simplementdonneesJson
) car cela vient de l'API dewordnik
- nous avons utilisé la syntaxe abrégée des fonctions fléchées, mais il va falloir ajouter des return dans chaque
.then()