Les promesses en ECMAScript 2015, ce qu’il faut savoir
Définition
L’objet Promise (pour « promesse ») est utilisé pour réaliser des traitements de façon asynchrone. Une promesse représente une unique opération asynchrone qui n’a pas encore été complétée, mais qui est attendue dans le futur.
MDN
Plus concrètement, les promesses sont des objets apportant une nouvelle syntaxe au javascript qui permet de mettre à plat ce qu’on appelle techniquement le “code spaghetti “.
Les promesses en javascript sont définies par un standard nommé Promises/A+ (https://promisesaplus.com/).
Il existe plusieurs librairies qui l’implémentent, plus ou moins fidèlement, telles que Q, RSVP.js ou Bluebird. Je vais cependant uniquement parler dans cet article de l’implémentation native des promesses apportées par l’ES6.
Promise 101
Commençons par quelques termes techniques. Une promesse peut avoir trois statuts différents : pending, resolved (ou fullfilled) et rejected. Il est possible d’instancier une promesse avec chacun de ces trois statuts.
Une promesse pending attendra sa résolution ou sa réjection.
Une promesse resolved appellera la fonction spécifiée avec la méthode then.
Une promesse rejected appellera la fonction spécifiée avec la méthode catch.
Pending promise
Pending promise about to be resolved
Une new Promise est résolue lorsque resolve est appelé.
Pending promise about to be rejected
Une new Promise est rejetée si reject est appelé, ou si throwest invoqué.
Resolved promise
Rejected promise
Promise wrapping
Les fonctionnalités natives de Javascript/Node.JS ne sont pas encore promise-based. Transformer des fonctions callback-based en fonctions promise-based est donc un bon entraînement et une étape primordiale dans le développement d’un projet promise-based.
En Node.JS, il existe des librairies qui se chargeront de convertir automatiquement des fonctions callback-based en fonctions promise-based, comme par exemple https://github.com/digitaldesignlabs/es6-promisify.
Enchaînement de promesses
Il y a quelques points importants à connaître à propos de l’enchaînement des promesses.
new Promise(function (resolve, reject) {}) retourne une promesse.
promise.then(function () {}) retourne une nouvelle promesse qui sera :
Résolue au retour de la fonction spécifiée en paramètre, ou si la fonction retourne une promesse résolue.Rejetée si un throw est appelé dans la fonction, ou si celle-ci retourne une promesse rejetée.
Lorsque la fonction spécifiée en paramètre de then retourne une promesse, sa résolution sera attendue avant la suite de l’enchaînement.
Dans l’exemple suivant, les promesses ne sont pas correctement chaînées, toutes les fonctions sont associées au thende la première promesse. Ces fonctions vont donc êtres appelées simultanément à la résolution de la promesse initiale.
Dans l’exemple suivant, l’enchaînement est préservé grâce à la conservation de chaque nouvelle promesse.
Transmission de variable
Il est possible de transmettre une unique variable à travers une promesse.
Error catching
La méthode catch permet d’intercepter les promesses rejetées.
A la suite d’un catch, l’enchaînement est préservé.
Il est également possible de spécifier une fonction de catch en la plaçant en deuxième argument de then :
Cependant, cette syntaxe fait perdre votre projet en lisibilité.
À lire sur ce sujet.
Si plusieurs catch sont présents, seul le premier sera appelé.
Enfin, vous pouvez transmettre l’exception au catch suivant grâce à un throw ou à un une promesse rejetée.
Attention cependant, si vous invoquez throw dans une fonction asynchrone, l’erreur ne sera pas catchée !
Il n’est pas obligatoire, mais recommandé, de throw un objet Error, car celui-ci embarque la stack d’exécution qui vous aidera à débugger.
promise.all
promise.all est une méthode prenant en paramètre un itérable (un Array par exemple) de promesses et renvoyant une promesse qui sera résolue lorsque toutes les promesses de l’itérable seront résolues.
Si l’une des promesses de l’itérable est rejetée, la promesse de promise.all le sera également.
promise.race
promise.race est une méthode prenant en paramètre un itérable de promesses et renvoyant une promesse qui sera résolue ou rejetée dès lors qu’une des promesses de l’itérable sera résolue ou rejetée.
Dans le cas suivant, p1 est résolue avant que p2 soit rejetée, la promesse finale est donc résolue car seul le premier résultat est pris en compte.
Commentaires