Published by

Il y a 8 ans -

Temps de lecture 5 minutes

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 :

[javascript gutter= »true »]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"[/javascript]

Bluebird permet de transformer le code précédent dans le code suivant :

[javascript gutter= »true »]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"[/javascript]

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 :

[javascript gutter= »true »]fs = require "fs"
Promise.promisifyAll fs

fs.readFileAsync("file.js", "utf8").then(…)[/javascript]

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 :

[javascript gutter= »true »]redisGet = Promise.promisify(redisClient.get, redisClient)
redisGet(‘foo’).then () ->
    #…[/javascript]

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 :

[javascript gutter= »true »]getDataFor(input, callback) ->
    dataFromDataBase(input).nodeify(callback)
[/javascript]

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 :

[javascript gutter= »true »]getDataFor("me").then (dataForMe) ->
    console.log dataForMe[/javascript]

Le même exemple exploitant le mécanisme de callback:

[javascript gutter= »true »]getDataFor "me", (err, dataForMe) ->
    if err
        console.error err
    console.log dataForMe[/javascript]

spread

En temps normal, le code suivant donnera en résultat la tableau : [1, 2, 3].

[javascript gutter= »true »]Promise.resolve([1,2,3]).nodeify (err, result) ->
    # err == null
    # result: [1,2,3]
[/javascript]

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:

[javascript gutter= »true »]Promise.resolve([1,2,3]).nodeify (err, a, b, c) ->
    # err == null
    # a == 1
    # b == 2
    # c == 3
, {spread: true}[/javascript]

Last but not least, la performance

Les librairies de promesses ont souvent été décriées pour le piètre performances. L’auteur de Bluebird s’est attaché à fournir une librairie performante aussi bien au niveau de l’usage du CPU que de la mémoire. Il décrit dans un article sur Github différents extraits de code tueurs de performance:

Les chiffres parlent plus qu’une longue explication :

Ceux-ci datent du mois de mai 2014. Il peuvent donc ne plus être complètement à jour, mais permettent de se faire une idée des performances obtenues avec Bluebird et de les comparer à d’autres librairies de promesses ou bien encore d’autres techniques asynchrones.

[bash] ├── async@0.7.0
├── davy@0.2.2
├── deferred@0.7.1
├── kew@0.4.0
├── lie@2.7.3
├── promise@5.0.0
├── q@1.0.1
├── rsvp@3.0.6
├── vow@0.4.3
└── when@3.1.0

bench doxbee-sequential

results for 10000 parallel executions, 1 ms per I/O op

file time(ms) memory(MB)
promises-bluebird-generator.js 171 16.52
callbacks-baseline.js 197 20.68
promises-bluebird.js 280 26.64
promises-lvivski-davy.js 616 58.75
promises-dfilatov-vow.js 672 80.59
promises-cujojs-when.js 731 68.74
callbacks-caolan-async-waterfall.js 733 44.57
promises-calvinmetcalf-lie.js 1035 113.07
promises-obvious-kew.js 1047 78.50
promises-tildeio-rsvp.js 1121 109.49
promises-ecmascript6-native.js 1298 96.05
promises-then-promise.js 1775 134.73
promises-medikoo-deferred.js 2238 149.61
promises-kriskowal-q.js 19786 415.04

Platform info:
Windows_NT 6.1.7601 ia32
Node.JS 0.11.13
V8 3.25.30
Intel(R) Core(TM) i5-2500K CPU @ 3.30GHz × 4

bench parallel (`–p 25`)

results for 10000 parallel executions, 1 ms per I/O op

file time(ms) memory(MB)
promises-bluebird.js 483 63.32
callbacks-baseline.js 545 25.54
promises-bluebird-generator.js 574 64.66
promises-lvivski-davy.js 1088 128.62
promises-cujojs-when.js 1527 178.57
callbacks-caolan-async-parallel.js 1635 99.87
promises-dfilatov-vow.js 1753 196.96
promises-then-promise.js 2553 338.36
promises-ecmascript6-native.js 3749 309.55
promises-obvious-kew.js 3805 366.32
promises-tildeio-rsvp.js 3916 462.23
promises-calvinmetcalf-lie.js 4477 230.99
promises-medikoo-deferred.js 4613 356.03

Platform info:
Windows_NT 6.1.7601 ia32
Node.JS 0.11.13
V8 3.25.30
Intel(R) Core(TM) i5-2500K CPU @ 3.30GHz × 4[/bash]

Le lien : https://github.com/petkaantonov/bluebird/blob/master/benchmark/stats/latest.md

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

Si vous n’utilisez pas les promesses ou n’êtes pas encore tout à fait convaincu de leur intérêt, vous pouvez en lire plus au sujet des promesses dans l’article suivant : http://spion.github.io/posts/why-i-am-switching-to-promises.html

Published by

Publié par Alexis Kinsella

Alexis Kinsella est un consultant Xebia passionné aussi bien par les problématiques frontend (web et mobile) que backend. Après de longues années passées sur les technologies Java, Alexis a fait d'iOS, Node.js et du Cloud ( AWS Certified Solutions Architect - Associate ) ses nouveaux terrains d'expérimentation lui permettant d'explorer les architectures web et mobiles ainsi que leurs problématiques associées.

Commentaire

0 réponses pour " Transformez votre code Node.js grâce au module de promises Bluebird "

  1. Published by , Il y a 7 ans

    Bon article, je cherchais justement de la doc sur ce module. Thanks !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.