Das alte Sprichwort besagt, dass Sie das richtige Werkzeug für den Job auswählen sollten. ES6-Versprechen liefern die Grundlagen. Wenn alles, was Sie jemals wollen oder brauchen, die Grundlagen sind, dann sollte / könnte das für Sie gut funktionieren. Es gibt jedoch mehr Werkzeuge im Werkzeugkorb als nur die Grundlagen, und es gibt Situationen, in denen diese zusätzlichen Werkzeuge sehr nützlich sind. Und ich würde argumentieren, dass ES6-Versprechen sogar einige der Grundlagen wie die Versprechen fehlen, die in so ziemlich jedem node.js-Projekt nützlich sind.
Ich bin mit der Bluebird-Versprechensbibliothek am besten vertraut, daher werde ich hauptsächlich aus meiner Erfahrung mit dieser Bibliothek sprechen.
Hier sind meine sechs wichtigsten Gründe, eine leistungsfähigere Promise-Bibliothek zu verwenden
Nicht versprochene asynchrone Schnittstellen - .promisify()
und .promisifyAll()
sind unglaublich nützlich, um all jene asynchronen Schnittstellen zu handhaben, die noch einfache Rückrufe erfordern und noch keine Versprechen zurückgeben - eine Codezeile erstellt eine versprochene Version einer gesamten Schnittstelle.
Schneller - Bluebird ist in den meisten Umgebungen deutlich schneller als native Versprechen.
Sequenzierung der Iteration eines asynchronen Arrays - Promise.mapSeries()
oder Promise.reduce()
Sie können ein Array durchlaufen, indem Sie für jedes Element eine asynchrone Operation aufrufen, die asynchronen Operationen jedoch so sequenzieren, dass sie nacheinander und nicht alle gleichzeitig ausgeführt werden. Sie können dies entweder tun, weil der Zielserver dies erfordert oder weil Sie ein Ergebnis an das nächste übergeben müssen.
Polyfill - Wenn Sie Versprechen in älteren Versionen von Browser-Clients verwenden möchten, benötigen Sie ohnehin eine Polyfill. Kann auch eine fähige Polyfüllung bekommen. Da node.js ES6-Versprechen hat, benötigen Sie keine Polyfüllung in node.js, aber möglicherweise in einem Browser. Wenn Sie sowohl den Server als auch den Client von node.js codieren, kann es sehr nützlich sein, in beiden dieselbe Versprechungsbibliothek und dieselben Funktionen zu haben (einfacherer Code-Austausch, Kontextwechsel zwischen Umgebungen, Verwendung gängiger Codierungstechniken für asynchronen Code usw.) .).
Weitere nützliche Funktionen - Drossel hat Promise.map()
, Promise.some()
, Promise.any()
, Promise.filter()
, Promise.each()
und Promise.props()
alle sind gelegentlich praktisch. Während diese Vorgänge mit ES6-Versprechungen und zusätzlichem Code ausgeführt werden können, enthält Bluebird diese Vorgänge bereits vorgefertigt und vorab getestet, sodass die Verwendung einfacher und weniger Code ist.
Eingebaute Warnungen und vollständige Stapelspuren - Bluebird verfügt über eine Reihe integrierter Warnungen, die Sie auf Probleme aufmerksam machen, bei denen es sich wahrscheinlich um falschen Code oder einen Fehler handelt. Wenn Sie beispielsweise eine Funktion aufrufen, die ein neues Versprechen in einem .then()
Handler erstellt, ohne dieses Versprechen zurückzugeben (um es mit der aktuellen Versprechen-Kette zu verknüpfen), handelt es sich in den meisten Fällen um einen versehentlichen Fehler, und Bluebird gibt Ihnen eine Warnung dazu aus bewirken. Weitere integrierte Bluebird-Warnungen werden hier beschrieben .
Hier einige Details zu diesen verschiedenen Themen:
PromisifyAll
In jedem node.js-Projekt verwende ich Bluebird sofort überall, da ich häufig .promisifyAll()
Standardmodule von node.js wie das fs
Modul verwende.
Node.js selbst bietet keine vielversprechende Schnittstelle zu den integrierten Modulen, die wie das fs
Modul asynchrone E / A-Vorgänge ausführen . Wenn Sie also Versprechen mit diesen Schnittstellen verwenden möchten, müssen Sie entweder einen Versprechen-Wrapper für jede von Ihnen verwendete Modulfunktion von Hand codieren oder eine Bibliothek erhalten, die dies für Sie tun kann oder keine Versprechen verwendet.
Bluebirds Promise.promisify()
und Promise.promisifyAll()
bieten eine automatische Umhüllung von node.js, die konventionelle asynchrone APIs aufrufen, um Versprechen zurückzugeben. Es ist äußerst nützlich und zeitsparend. Ich benutze es die ganze Zeit.
Hier ist ein Beispiel, wie das funktioniert:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Die Alternative wäre, manuell einen eigenen Versprechen-Wrapper für jede fs
API zu erstellen, die Sie verwenden möchten :
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
});
Und Sie müssen dies manuell für jede API-Funktion tun, die Sie verwenden möchten. Das macht eindeutig keinen Sinn. Es ist Boilerplate-Code. Sie können auch ein Dienstprogramm erwerben, das diese Aufgabe für Sie erledigt. Bluebird's Promise.promisify()
und Promise.promisifyAll()
sind so ein Dienstprogramm.
Andere nützliche Funktionen
Hier sind einige der Bluebird-Funktionen, die ich besonders nützlich finde (im Folgenden finden Sie einige Codebeispiele, wie diese Code speichern oder die Entwicklung beschleunigen können):
Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()
Zusätzlich zu seiner nützlichen Funktion wird Promise.map()
auch eine Parallelitätsoption unterstützt, mit der Sie festlegen können, wie viele Vorgänge gleichzeitig ausgeführt werden sollen. Dies ist besonders nützlich, wenn Sie viel zu tun haben, aber einige außerhalb nicht überwältigen können Ressource.
Einige davon können sowohl als eigenständig bezeichnet als auch für ein Versprechen verwendet werden, das sich selbst in eine iterierbare Version auflöst, die viel Code sparen kann.
Polyfill
In einem Browserprojekt benötigen Sie ohnehin eine Polyfüllung, da Sie im Allgemeinen einige Browser unterstützen möchten, die keine Promise-Unterstützung bieten. Wenn Sie auch jQuery verwenden, können Sie manchmal einfach die in jQuery integrierte Versprechensunterstützung verwenden (obwohl dies in gewisser Weise schmerzlich nicht dem Standard entspricht, möglicherweise in jQuery 3.0 behoben), aber wenn das Projekt eine signifikante asynchrone Aktivität beinhaltet, finde ich Die erweiterten Funktionen in Bluebird sind sehr nützlich.
Schneller
Erwähnenswert ist auch, dass Bluebirds Versprechen deutlich schneller zu sein scheinen als die in V8 integrierten Versprechen. In diesem Beitrag finden Sie weitere Informationen zu diesem Thema.
Ein Big Thing Node.js fehlt
Was mich dazu bringen würde, Bluebird weniger in der Entwicklung von node.js zu verwenden, wäre, wenn node.js in eine Promisify-Funktion eingebaut wäre, damit Sie so etwas tun könnten:
const fs = requirep('fs');
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Oder bieten Sie einfach bereits versprochene Methoden als Teil der eingebauten Module an.
Bis dahin mache ich das mit Bluebird:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Es scheint ein bisschen seltsam zu sein, dass die Unterstützung von ES6-Versprechen in node.js integriert ist und keines der integrierten Module Versprechen zurückgibt. Dies muss in node.js aussortiert werden. Bis dahin benutze ich Bluebird, um ganze Bibliotheken zu versprechen. Es scheint also, dass Versprechen jetzt zu etwa 20% in node.js implementiert sind, da Sie mit keinem der integrierten Module Versprechen mit ihnen verwenden können, ohne sie zuerst manuell zu verpacken.
Beispiele
Hier ist ein Beispiel für einfache Versprechen im Vergleich zu Bluebirds Versprechen und Promise.map()
zum parallelen Lesen einer Reihe von Dateien und zum Benachrichtigen, wenn alle Daten fertig sind:
Einfache Versprechen
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()
undPromise.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
});
Hier ist ein Beispiel für einfache Versprechen im Vergleich zu Bluebirds Versprechen und Promise.map()
beim Lesen einer Reihe von URLs von einem Remote-Host, auf dem Sie höchstens 4 gleichzeitig lesen können, aber so viele Anfragen wie möglich parallel halten möchten:
Einfache JS-Versprechen
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 verspricht
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
});