author
Lorsqu’on parle de promises dans l’écosystème Node.js, on pense immédiatement à la librairie Q. Toutefois, il existe de nombreux modules de promises proposant chacun des choses différentes. En particulier, le module bluebird se démarque grâce à des fonctionnalités tout à fait intéressantes telles que la “promisification”.
Promisification
Les core modules de Node.js fonctionnent à base de callback. Ainsi pour lire un fichier de façon asynchrone, il faut appeler la fonction readFile du module fs et traiter la réponse depuis le callback passé en dernier paramètre de la fonction lors de son appel:
fs.readFile "file.json", (err, val) ->
if err
console.error "unable to read file"
try
val = JSON.parse(val);
console.log val.success
catch e
console.error "invalid json in file"
Bluebird permet de transformer le code précédent dans le code suivant:
fs.readFileAsync("file.json").then(JSON.parse).then (val) ->
console.log val.success
.catch SyntaxError, (e) ->
console.error "invalid json in file"
.catch (e) ->
console.error "unable to read file"
Cette transformation est rendue possible grâce à la promisification du module fs, via l’appel de la fonction promisifyAll qui permet de transformer toutes les fonctions exposées en fonctions renvoyant des promises:
fs = require "fs"
Promise.promisifyAll fs
fs.readFileAsync("file.js", "utf8").then(...)
Selon toute vraisemblance, les fonctions du modules sont proxifiées via un wrapping changeant la signature.On pourra noter que le chaînage de fonctions catch sur la promise permet de différencier le traitement des erreurs en fonction de leur type. Ici, l’erreur de type SyntaxError est traitée différemment des erreurs typées autrement.
promisify
Il est également possible de ne promisifier qu’une seule fonction grâce à la fonction promisify:
redisGet = Promise.promisify(redisClient.get, redisClient)redisGet('foo').then () -> #...
Il y a tout de même un piège puisque la fonction attend 2 paramètres. Le premier étant la référence de la fonction à promisifier, et le second étant l’objet auquel la fonction est rattachée.
nodeify
La fonction nodeify est également très intéressante car elle permet d’enregistrer un callback sur une promise bluebird et d’appeler celui-ci à la résolution de cette dernière:
getDataFor(input, callback) ->
dataFromDataBase(input).nodeify(callback)
Cette possibilité est particulièrement intéressante, car elle permet de construire des API qui deviennent utilisables aussi bien par du code qui fonctionne à base de callback, qu’avec du code à base de promise.
Ainsi, si le callback est renseigné, il sera appelé. Sinon, il suffira d’exploiter la promise retournée par la fonction pour obtenir et traiter le résultat de l’appel.
Exemple exploitant le mécanisme de promise:
getDataFor("me").then (dataForMe) ->
console.log dataForMe
Le même exemple exploitant le mécanisme de callback:
getDataFor "me", (err, dataForMe) ->
if err
console.error err
console.log dataForMe
spread
En temps normal, le code suivant donnera en résultat la tableau : [1, 2, 3].
Promise.resolve([1,2,3]).nodeify (err, result) ->
# err == null
# result: [1,2,3]
Toutefois, l’option {spread: true} passée à l’appel de la fonction nodeify, permet de dispatcher les valeurs de résultat sur l’ensemble des arguments de la fonction de callback renseignée:
Promise.resolve([1,2,3]).nodeify (err, a, b, c) ->
# err == null
# a == 1
# b == 2
# c == 3
, {spread: true}
Conclusion
La librairie bluebird est riche en fonctions pour le moins intéressantes, vous pouvez les retrouver sur la page de documentation du projet GitHub:
Lien: https://github.com/petkaantonov/bluebird/blob/master/API.md