Réaliser des promesses Javascript avec Bluebird
Une promesse c’est quoi?
Une promesse permet de représenter la promesse de résultat d’une action asynchrone. Rechercher dans une base de données, faire une requête HTTP pour mettre à jour les données de sa vue ou d’autres actions doivent être faites de manière asynchrone pour éviter qu’une application ne soit figée pour l’utilisateur.
Les promesses sont décrites dans Promises/A+.
Une promesse est un objet à 3 états: pending, fulfilled, rejected
Quand une promesse est en cours (pending), elle peut uniquement changer d’état ou attendre. On peut avoir une réponse (fulfilled) ou une erreur (rejected).
- Une promesse tenue possède une valeur associée qui est immuable.
- Une promesse rejetée possède la raison de rejet immuable également.
Une promesse possède aussi une méthode then qui permet de réaliser une action lorsque la promesse est dans un état final (fulfilled, rejected). Elle est de la forme:
promise.then(onFulfilled, onRejected)
onFulfilled et onRejected sont habituellement des fonctions mais sont optionnelles. Une personne peut donc créer facilement des flux d’action asynchrone à réaliser à la chaîne.
promise <---- une fonction à réaliser
.then(doSomething) <---- ce qu'il doit faire ensuite
.then(doSomethingElse, doSomethingIfRejected) <---- et après...
.then(...)
....
On peut donc exprimer des traitements complexes avec une gestion des erreurs plus simple ainsi qu’avec une vision très proche de la programmation synchrone. On voit très bien le fil d’exécution.
Et Bluebird ?
C’est une des nombreuses implémentations des Promesses en Javascript. On y retrouve les méthodes obligatoires pour toute promesse et aussi d’autres méthodes pour l’enrichir.
Bluebird
Voici une liste de fonctionnalités particulièrement utile.
- .finally permet d’effectuer une action quelque soit le résultat.
- .catch permet de réalisé une gestion simple des erreurs. C’est un raccourcis pour .then(null, errorHandler).
- Promise.all revoie une promesse qui sera tenue si toute les promesses indiquées en paramètre sont tenues. En cas de rejet d’une des promesses de la collection en entrée, la raison de la première promesse rejetée sera transmise comme résultat.
- Promise.map permet de mapper un Iterable ou une promesse d’un Iterable avec un générateur de promesse (une fonction qui retourne une promesse) pour fabriquer une liste de promesses. Si une promesse est rejetée, alors l’ensemble des promesses est rejeté.
- Promise.some et son homologue Promise.any permettent de créer une promesse qui est tenue si N promesses parmi l’ensemble sont tenues (N=1 pour .any)
Bluebird ajoute des méthodes supplémentaires pour réaliser des requêtes sur les promesses. On peut demander si une promesse est dans un état particulier (ex: .isFulfilled renvoie un booléen). On peut récupérer la valeur retournée par une promesse tenue avec la méthode .value ou bien récupérer la raison d’un rejet retourné à l’aide de la méthode .reason . On peut également annuler une promesse à l’aide de la méthode .cancel .
Annuler une promesse permet, d’un point de vue utilisateur, d’annuler une action en cours tant qu’elle est encore en attente.
Toute la documentation de Bluebird est disponible sur leur site
Quelques exemples d’utilisation ( CoffeeScript)
Changer une adresse mail et envoyer un mail de confirmation
Quelques points :
- res est une réponse HTTP
- reply renvoie une promesse qui émet res si updatePromise est tenue et res est une réponse 4xx si updatePromise est rejetée
- .exec() renvoie une promesse et provient de Mongoose
- Promise.all contient 3 promesses
- en cas de rejet de Promise.all, on affiche une erreur
updatePromise est une promesse qui met à jour notre base de donnée. On a donc une promesse qui engendre 3 promesses en parallèle.
Réaliser des requêtes HTTP à une API REST
- $http.get réalise une requête HTTP GET et retourne une promesse (voir doc AngularJS)
- On retourne les données contenues dans la réponse
En conclusion
Les promesses sont un très bon moyen de réaliser des actions asynchrones. Plus facile à prendre en main que les Callbacks car elles sont plus proche de la programmation synchrone en terme de syntaxe, elles permettent également de gérer plus facilement les erreurs.
En complément, Forbes Lindesay a réalisé une présentation de son implémentation des Promesses : www.promisejs.org lors d’une JSConf que je conseille de regarder.