Y a-t-il encore des raisons d'utiliser des bibliothèques de promesses comme Q ou BlueBird maintenant que nous avons des promesses ES6? [fermé]

229

Après que Node.js ait ajouté le support natif pour les promesses, y a-t-il encore des raisons d'utiliser des bibliothèques comme Q ou BlueBird?

Par exemple, si vous démarrez un nouveau projet et supposons que dans ce projet vous n'avez pas de dépendances qui utilisent ces bibliothèques, pouvons-nous dire qu'il n'y a vraiment plus de raisons d'utiliser de telles bibliothèques?

Murat Ozgul
la source
4
les promesses natives ont des caractéristiques très très basiques. Des bibliothèques comme Q ou Bluebird en ajoutent beaucoup plus. Si vous avez besoin de ces fonctionnalités, utilisez ces bibliothèques.
gman
7
J'ai édité le titre pour le rendre moins sur le «besoin» et plus sur les «raisons d'utiliser toujours les bibliothèques de promesses». On peut répondre à cette question en fournissant principalement des faits, pas une opinion. Il doit être rouvert car il est possible d'y répondre en fournissant des faits et non principalement des opinions. Voir la réponse ci-dessous comme une démonstration de cela.
jfriend00
11
@JaromandaX - Veuillez envisager de rouvrir maintenant que le titre et la question ont été modifiés pour être plus sur pourquoi on utiliserait une bibliothèque de promesses plutôt que si on "a besoin" d'utiliser une bibliothèque de promesses. À mon avis, il est possible de répondre à cette question en fournissant des faits et non principalement une opinion - voir la réponse ci-dessous pour en faire la démonstration.
jfriend00
6
Cette question après la modification du titre et sa réponse acceptée ne sont pas basées sur des opinions.
max
7
D'accord. C'est une question parfaitement valable dans sa forme actuelle. J'ai proposé ma réouverture.
Jules

Réponses:

368

Le vieil adage veut que vous devez choisir le bon outil pour le travail. Les promesses d'ES6 fournissent les bases. Si tout ce que vous voulez ou avez besoin est les bases, alors cela devrait / pourrait fonctionner très bien pour vous. Mais, il y a plus d'outils dans la boîte à outils que les bases et il y a des situations où ces outils supplémentaires sont très utiles. Et, je dirais que les promesses ES6 manquent même certaines des bases comme la promisification qui sont utiles dans à peu près tous les projets node.js.

Je connais le mieux la bibliothèque de promesses Bluebird , je parlerai donc principalement de mon expérience avec cette bibliothèque.

Voici donc mes 6 principales raisons d'utiliser une bibliothèque Promise plus performante

  1. Interfaces asynchrones non promises - .promisify()et .promisifyAll()sont incroyablement utiles pour gérer toutes ces interfaces asynchrones qui nécessitent toujours des rappels simples et ne renvoient pas encore de promesses - une ligne de code crée une version promise de toute une interface.

  2. Plus rapide - Bluebird est nettement plus rapide que les promesses natives dans la plupart des environnements.

  3. Séquençage de l'itération du tableau asynchrone - Promise.mapSeries()ou Promise.reduce()vous permet de parcourir un tableau, en appelant une opération asynchrone sur chaque élément, mais en séquençant les opérations asynchrones de sorte qu'elles se produisent l'une après l'autre, pas toutes en même temps. Vous pouvez le faire soit parce que le serveur de destination l'exige, soit parce que vous devez passer un résultat au suivant.

  4. Polyfill - Si vous souhaitez utiliser des promesses dans les anciennes versions des clients de navigateur, vous aurez quand même besoin d'un polyfill. Peut aussi bien obtenir un polyfill capable. Étant donné que node.js a des promesses ES6, vous n'avez pas besoin d'un polyfill dans node.js, mais vous pouvez le faire dans un navigateur. Si vous codez à la fois le serveur et le client node.js, il peut être très utile d'avoir la même bibliothèque de promesses et les mêmes fonctionnalités dans les deux (plus facile à partager du code, changement de contexte entre les environnements, utiliser des techniques de codage communes pour le code asynchrone, etc.) .).

  5. Autres fonctions - Bluebird a Promise.map(), Promise.some(), Promise.any(), Promise.filter(), Promise.each()etPromise.props() tous qui sont parfois à portée de main. Bien que ces opérations puissent être effectuées avec des promesses ES6 et du code supplémentaire, Bluebird est livré avec ces opérations déjà pré-construites et pré-testées, il est donc plus simple et moins de code de les utiliser.

  6. Avertissements intégrés et traces de pile complètes - Bluebird a un certain nombre d'avertissements intégrés qui vous avertissent de problèmes qui sont probablement du mauvais code ou un bogue. Par exemple, si vous appelez une fonction qui crée une nouvelle promesse à l'intérieur d'un .then()gestionnaire sans renvoyer cette promesse (pour la lier à la chaîne de promesses actuelle), alors dans la plupart des cas, il s'agit d'un bogue accidentel et Bluebird vous en avertira effet. D'autres avertissements Bluebird intégrés sont décrits ici .

Voici quelques détails supplémentaires sur ces différents sujets:

PromisifyAll

Dans tout projet node.js, j'utilise immédiatement Bluebird partout car j'utilise .promisifyAll()beaucoup de modules node.js standard comme le fsmodule.

Node.js ne fournit pas lui-même une interface de promesse aux modules intégrés qui font des E / S asynchrones comme le fs module. Donc, si vous souhaitez utiliser des promesses avec ces interfaces, vous devez soit coder manuellement un wrapper de promesse autour de chaque fonction de module que vous utilisez, soit obtenir une bibliothèque qui peut le faire pour vous ou ne pas utiliser de promesses.

Bluebird Promise.promisify()et Promise.promisifyAll()fournir un habillage automatique de node.js appelant les API asynchrones de convention pour retourner les promesses. C'est extrêmement utile et fait gagner du temps. Je l'utilise tout le temps.

Voici un exemple de comment cela fonctionne:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

fs.readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

L'alternative serait de créer manuellement votre propre wrapper de promesse pour chaque fsAPI que vous souhaitez utiliser:

const fs = require('fs');

function readFileAsync(file, options) {
    return new Promise(function(resolve, reject) {
        fs.readFile(file, options, function(err, data) {
            if (err) {
                reject(err);
            } else {
                 resolve(data);
            }
        });
    });
}

readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

Et, vous devez le faire manuellement pour chaque fonction API que vous souhaitez utiliser. Cela n'a clairement aucun sens. C'est le code passe-partout. Vous pourriez aussi bien obtenir un utilitaire qui fait ce travail pour vous. Bluebird Promise.promisify()et Promise.promisifyAll()sont un tel utilitaire.

Autres fonctionnalités utiles

Voici quelques-unes des fonctionnalités de Bluebird que je trouve particulièrement utiles (il y a quelques exemples de code ci-dessous sur la façon dont ceux-ci peuvent enregistrer du code ou accélérer le développement):

Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()

En plus de sa fonction utile, Promise.map()prend également en charge une option de concurrence qui vous permet de spécifier le nombre d'opérations qui doivent être autorisées à s'exécuter en même temps, ce qui est particulièrement utile lorsque vous avez beaucoup de choses à faire, mais ne pouvez pas en submerger certaines à l'extérieur Ressource.

Certains d'entre eux peuvent être à la fois appelés autonomes et utilisés sur une promesse qui se résout en un itérable qui peut économiser beaucoup de code.


Polyfill

Dans un projet de navigateur, étant donné que vous souhaitez généralement prendre en charge certains navigateurs qui ne prennent pas en charge Promise, vous finissez par avoir besoin d'un polyfill de toute façon. Si vous utilisez également jQuery, vous pouvez parfois simplement utiliser le support de promesse intégré à jQuery (bien qu'il soit douloureusement non standard à certains égards, peut-être corrigé dans jQuery 3.0), mais si le projet implique une activité asynchrone importante, je trouve les fonctionnalités étendues de Bluebird sont très utiles.


plus rapide

Il convient également de noter que les promesses de Bluebird semblent être beaucoup plus rapides que les promesses intégrées au V8. Voir cet article pour plus de discussion sur ce sujet.


Une grosse chose Node.js est manquante

Ce qui me ferait envisager d'utiliser moins Bluebird dans le développement de node.js serait si node.js a intégré une fonction promisify pour que vous puissiez faire quelque chose comme ceci:

const fs = requirep('fs');

fs.readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

Ou offrez simplement des méthodes déjà promises dans le cadre des modules intégrés.

Jusque-là, je fais ça avec Bluebird:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

fs.readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

Il semble un peu étrange que le support des promesses ES6 soit intégré dans node.js et qu'aucun des modules intégrés ne renvoie de promesses. Cela doit être trié dans node.js. D'ici là, j'utilise Bluebird pour promettre des bibliothèques entières. Ainsi, il semble que les promesses soient implémentées à environ 20% dans node.js maintenant, car aucun des modules intégrés ne vous permet d'utiliser des promesses avec elles sans les envelopper manuellement au préalable.


Exemples

Voici un exemple de promesses simples par rapport à la promesse de Bluebird et Promise.map()pour la lecture d'un ensemble de fichiers en parallèle et la notification lorsque vous avez terminé avec toutes les données:

Des promesses simples

const files = ["file1.txt", "fileA.txt", "fileB.txt"];
const fs = require('fs');

// make promise version of fs.readFile()
function fsReadFileP(file, options) {
    return new Promise(function(resolve, reject) {
        fs.readFile(file, options, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}


Promise.all(files.map(fsReadFileP)).then(function(results) {
    // files data in results Array
}, function(err) {
    // error here
});

Bluebird Promise.map()etPromise.promisifyAll()

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const files = ["file1.txt", "fileA.txt", "fileB.txt"];

Promise.map(files, fs.readFileAsync).then(function(results) {
    // files data in results Array
}, function(err) {
    // error here
});

Voici un exemple de promesses simples par rapport à la promesse de Bluebird et Promise.map()lors de la lecture d'un tas d'URL à partir d'un hôte distant où vous pouvez lire au plus 4 à la fois, mais souhaitez conserver autant de demandes en parallèle que possible:

Plain JS Promises

const request = require('request');
const urls = [url1, url2, url3, url4, url5, ....];

// make promisified version of request.get()
function requestGetP(url) {
    return new Promise(function(resolve, reject) {
        request.get(url, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}

function getURLs(urlArray, concurrentLimit) {
    var numInFlight = 0;
    var index = 0;
    var results = new Array(urlArray.length);
    return new Promise(function(resolve, reject) {
        function next() {
            // load more until concurrentLimit is reached or until we got to the last one
            while (numInFlight < concurrentLimit && index < urlArray.length) {
                (function(i) {
                    requestGetP(urlArray[index++]).then(function(data) {
                        --numInFlight;
                        results[i] = data;
                        next();
                    }, function(err) {
                        reject(err);
                    });
                    ++numInFlight;
                })(index);
            }
            // since we always call next() upon completion of a request, we can test here
            // to see if there was nothing left to do or finish
            if (numInFlight === 0 && index === urlArray.length) {
                resolve(results);
            }
        }
        next();
    });
}

Promesses Bluebird

const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'));
const urls = [url1, url2, url3, url4, url5, ....];

Promise.map(urls, request.getAsync, {concurrency: 4}).then(function(results) {
    // urls fetched in order in results Array
}, function(err) {
    // error here
});
jfriend00
la source
bien qu'il soit douloureusement non standard à certains égards - Ils affirment qu'ils sont désormais "Promises / A + compatibles" :) - blog.jquery.com/2016/01/14/jquery-3-0-beta-released
thefourtheye
1
@thefourtheye - Oui, je sais qu'ils ont travaillé vers la compatibilité Promise / A + en 3.0. Mais c'est toujours en version bêta. Si elle est à la hauteur de la promesse (jeu de mots voulu), cela peut éviter une partie de la raison d'utiliser une bibliothèque de promesses externe dans le navigateur JS si vous utilisiez déjà jQuery. Il n'aura toujours pas toutes les fonctionnalités utiles de Bluebird et je serais extrêmement surpris s'il était à la hauteur des performances de Bluebird, il y a donc encore de la place pour Bluebird aux côtés d'une future jQuery dans certains cas. Dans tous les cas, la question du PO semble concerner principalement node.js.
jfriend00
1
Il y a une petite erreur dans le dernier exemple de code: return new Promise(function(resolve, rejct). Devrait être:reject
Sebastian Muszyński
7
Node.js en a util.promisifymaintenant, bien qu'il n'y ait pas d' promisifyAlléquivalent direct .
nyuszika7h
1
@Aurast - Oui, la v11 prend en charge fs, mais encore d'autres raisons d'utiliser Bluebird (mon préféré est l' concurrencyoption en Promise.map()) pour éviter de submerger un service cible auquel vous devez faire un tas de demandes parallèles. De plus, il y a encore beaucoup d'autres interfaces non promises pour utiliser promisifyAll de Bluebird. Mais, lentement, les raisons de saisir immédiatement Bluebird dans chaque nouveau projet s'effacent alors que node.js lui-même renforce son support de promesse intégré.
jfriend00