Nodes.js

Nodes.js

Transformez votre code Node.js grâce au module de promises Bluebird

Transformez votre code Node.js grâce au module de promises Bluebird

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"

promisifyAll

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