Ci sono ancora motivi per usare librerie di promesse come Q o BlueBird ora che abbiamo promesse ES6? [chiuso]


229

Dopo che Node.js ha aggiunto il supporto nativo per le promesse, ci sono ancora motivi per usare librerie come Q o BlueBird?

Ad esempio, se stai avviando un nuovo progetto e supponiamo che in questo progetto non ci siano dipendenze che utilizzano queste librerie, possiamo dire che non ci sono davvero più motivi per usare tali librerie?


4
le promesse native hanno caratteristiche molto basilari. Le biblioteche come Q o Bluebird ne aggiungono molte altre. Se hai bisogno di quelle funzionalità, usa quelle librerie.
Gman,

7
Ho modificato il titolo per renderlo meno "necessario" e più "ragioni per usare ancora le librerie promesse". A questa domanda si può rispondere fornendo principalmente fatti, non opinioni. Dovrebbe essere riaperto perché si può rispondere fornendo fatti e non principalmente opinioni. Vedi la risposta di seguito come dimostrazione di ciò.
jfriend00,

11
@JaromandaX - Per favore, considera di riaprire ora che il titolo e la domanda sono stati modificati per essere più sul motivo per cui uno dovrebbe usare una libreria di promesse piuttosto che se "ha bisogno" di usare una libreria di promesse. A mio avviso, a questa domanda si può rispondere fornendo fatti e non principalmente opinioni - vedere la risposta di seguito come dimostrazione di ciò.
jfriend00,

6
Questa domanda dopo la modifica del titolo e la sua risposta accettata non sono basate sull'opinione.
max

7
Concordato. Questa è una domanda perfettamente valida nella sua forma attuale. Ho nominato per la riapertura.
Jules,

Risposte:


368

Il vecchio adagio dice che dovresti scegliere lo strumento giusto per il lavoro. Le promesse ES6 forniscono le basi. Se tutto ciò che desideri o hai bisogno sono le basi, allora dovrebbe / potrebbe funzionare bene per te. Ma ci sono più strumenti nel cestino degli strumenti oltre alle nozioni di base e ci sono situazioni in cui quegli strumenti aggiuntivi sono molto utili. E direi che le promesse di ES6 mancano anche di alcune nozioni di base come la promisificazione, utili in quasi tutti i progetti node.js.

Conosco molto bene la biblioteca di promesse Bluebird, quindi parlerò principalmente della mia esperienza con quella biblioteca.

Quindi, ecco i miei 6 principali motivi per utilizzare una libreria Promise più capace

  1. Interfacce asincrone non promesse - .promisify()e .promisifyAll()sono incredibilmente utili per gestire tutte quelle interfacce asincrone che richiedono ancora semplici callback e non restituiscono ancora promesse - una riga di codice crea una versione promessa di un'intera interfaccia.

  2. Più veloce - Bluebird è significativamente più veloce delle promesse native nella maggior parte degli ambienti.

  3. Sequenziamento dell'iterazione asincrona dell'array - Promise.mapSeries()oppure Promise.reduce()consente di iterare attraverso un array, chiamando un'operazione asincrona su ciascun elemento, ma sequenziando le operazioni asincrone in modo che avvengano una dopo l'altra, non tutte contemporaneamente. È possibile farlo perché il server di destinazione lo richiede o perché è necessario passare un risultato al successivo.

  4. Polyfill : se si desidera utilizzare le promesse nelle versioni precedenti dei client browser, sarà comunque necessario un polyfill. Può anche ottenere un polyfill capace. Poiché node.js ha promesse ES6, non è necessario un polyfill in node.js, ma è possibile che sia presente in un browser. Se stai codificando sia il server sia il client node.js, può essere molto utile avere la stessa libreria promessa e le stesse funzionalità in entrambi (più facile condividere il codice, cambiare contesto tra ambienti, usare tecniche di codifica comuni per codice asincrono, ecc. .).

  5. Altre funzioni utili - Bluebird ha Promise.map(), Promise.some(), Promise.any(), Promise.filter(), Promise.each()e Promise.props()che sono tutti a portata di mano di tanto in tanto. Mentre queste operazioni possono essere eseguite con promesse ES6 e codice aggiuntivo, Bluebird viene fornito con queste operazioni già pre-costruite e pre-testate, quindi è più semplice e meno codice usarle.

  6. Avvisi integrati e tracce di stack completo : Bluebird ha un numero di avvisi integrati che avvisano di problemi che probabilmente sono codice errato o un bug. Ad esempio, se chiami una funzione che crea una nuova promessa all'interno di un .then()gestore senza restituire quella promessa (per collegarla all'attuale catena di promesse), nella maggior parte dei casi si tratta di un bug accidentale e Bluebird ti avviserà di ciò effetto. Altri avvisi Bluebird integrati sono descritti qui .

Ecco alcuni dettagli in più su questi vari argomenti:

PromisifyAll

In qualsiasi progetto node.js, uso immediatamente Bluebird ovunque perché uso .promisifyAll()molto su moduli node.js standard come il fsmodulo.

Node.js non fornisce di per sé un'interfaccia promettente ai moduli integrati che eseguono l'asincronizzazione di IO come il fs modulo. Pertanto, se si desidera utilizzare le promesse con quelle interfacce, è possibile codificare manualmente un wrapper di promessa attorno a ciascuna funzione del modulo utilizzata o ottenere una libreria che può fare ciò per sé o non utilizzare le promesse.

Bluebird's Promise.promisify()ePromise.promisifyAll() fornisce un wrapping automatico di node.js che chiama le API asincrone della convenzione per restituire le promesse. È estremamente utile e fa risparmiare tempo. Io lo uso per tutto il tempo.

Ecco un esempio di come funziona:

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

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

L'alternativa sarebbe quella di creare manualmente il proprio wrapper di promessa per ciascuna fsAPI che si desidera utilizzare:

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
});

E devi farlo manualmente per ogni funzione API che vuoi usare. Questo chiaramente non ha senso. È il codice boilerplate. Potresti anche ottenere un'utilità che fa questo lavoro per te. Bluebird's Promise.promisify()ePromise.promisifyAll() sono una tale utilità.

Altre caratteristiche utili

Ecco alcune delle funzionalità di Bluebird che trovo particolarmente utili (di seguito sono riportati alcuni esempi di codice su come questi possono salvare il codice o accelerare lo sviluppo):

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

Oltre alla sua utile funzione, Promise.map()supporta anche un'opzione di concorrenza che ti consente di specificare quante operazioni devono essere eseguite contemporaneamente, il che è particolarmente utile quando hai molto da fare, ma non puoi sopraffarne alcune all'esterno risorsa.

Alcuni di questi possono essere entrambi definiti autonomi e utilizzati su una promessa che si risolve in un iterabile che può salvare molto codice.


polyfill

In un progetto browser, poiché in genere si desidera ancora supportare alcuni browser che non dispongono del supporto Promise, si finisce comunque per richiedere un polyfill. Se stai usando anche jQuery, a volte puoi semplicemente usare il supporto promessa integrato in jQuery (anche se è dolorosamente non standard in qualche modo, forse risolto in jQuery 3.0), ma se il progetto prevede attività asincrone significative, trovo le funzionalità estese di Bluebird sono molto utili.


Più veloce

Vale anche la pena notare che le promesse di Bluebird sembrano essere significativamente più veloci delle promesse integrate in V8. Vedi questo post per ulteriori discussioni su questo argomento.


Manca qualcosa di grosso Node.js

Ciò che mi farebbe pensare di usare Bluebird meno nello sviluppo di node.js sarebbe se node.js fosse incorporato in una funzione promisify in modo da poter fare qualcosa del genere:

const fs = requirep('fs');

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

O semplicemente offri metodi già promessi come parte dei moduli integrati.

Fino ad allora, lo faccio con Bluebird:

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

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

Sembra un po 'strano avere il supporto delle promesse ES6 integrato in node.js e nessuno dei moduli integrati restituisce promesse. Questo deve essere risolto in node.js. Fino ad allora, uso Bluebird per promettere intere librerie. Quindi, sembra che le promesse siano state implementate in circa il 20% in node.js ora poiché nessuno dei moduli integrati ti consente di utilizzare le promesse senza prima inserirle manualmente.


Esempi

Ecco un esempio di promesse semplici rispetto a quelle promesse da Bluebird e Promise.map()per la lettura di una serie di file in parallelo e la notifica al termine di tutti i dati:

Promesse semplici

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()ePromise.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
});

Ecco un esempio di promesse semplici rispetto a quelle promesse da Bluebird e Promise.map()quando si leggono un sacco di URL da un host remoto in cui è possibile leggere al massimo 4 alla volta, ma si desidera mantenere quante più richieste in parallelo consentite:

Promesse semplici di JS

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();
    });
}

Bluebird Promises

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
});

sebbene sia dolorosamente non standard in qualche modo - Ora sostengono di essere "Promesse / Compatibile A +" :) - blog.jquery.com/2016/01/14/jquery-3-0-beta-released
thefourtheye

1
@thefourtheye - Sì, lo so che hanno lavorato per la compatibilità Promise / A + in 3.0. Ma è ancora in beta. Se è all'altezza della promessa (gioco di parole intenzionale), potrebbe ovviare ad alcuni dei motivi per utilizzare una libreria di promesse esterne nel browser JS se si stava già utilizzando jQuery. Non avrà ancora tutte le funzionalità utili di Bluebird e sarei estremamente sorpreso se all'altezza delle prestazioni di Bluebird, quindi in alcuni casi c'è ancora spazio per Bluebird accanto a un futuro jQuery. In ogni caso, la domanda dell'OP sembra riguardare principalmente node.js.
jfriend00,

1
C'è un po 'errore di battitura nell'ultima codice di esempio: return new Promise(function(resolve, rejct). Dovrebbe essere:reject
Sebastian Muszyński

7
Node.js attualmente ha util.promisify, anche se non esiste un promisifyAllequivalente diretto .
nyuszika7h,

1
@Aurast - Sì, v11 si prende cura di fs, ma ancora alcuni altri motivi per usare Bluebird (il mio preferito è l' concurrencyopzione in Promise.map()) per evitare di schiacciare un servizio di destinazione a cui devi fare un sacco di richieste parallele. Inoltre, ci sono ancora molte altre interfacce non promesse per utilizzare promisify All di Bluebird. Ma, lentamente, i motivi per afferrare immediatamente Bluebird in ogni nuovo progetto stanno svanendo mentre node.js stesso rafforza il suo supporto promesso incorporato.
jfriend00
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.