Stare powiedzenie głosi, że należy wybrać odpowiednie narzędzie do pracy. Obietnice ES6 zapewniają podstawy. Jeśli wszystko, czego kiedykolwiek pragniesz lub potrzebujesz, to podstawy, to powinno / powinno działać dobrze dla Ciebie. Ale w koszu na narzędzia jest więcej narzędzi niż tylko podstawy i są sytuacje, w których te dodatkowe narzędzia są bardzo przydatne. I twierdzę, że w obietnicach ES6 brakuje nawet niektórych podstawowych elementów, takich jak obietnica, które są przydatne w prawie każdym projekcie node.js.
Najbardziej znam bibliotekę obietnic Bluebird więc będę rozmawiać głównie z mojego doświadczenia z tą biblioteką.
Oto moje 6 głównych powodów, dla których warto skorzystać z bardziej wydajnej biblioteki Promise
Niesamowite interfejsy asynchroniczne - .promisify()
i .promisifyAll()
są niezwykle przydatne do obsługi wszystkich tych interfejsów asynchronicznych, które wciąż wymagają zwykłych wywołań zwrotnych i jeszcze nie zwracają obietnic - jeden wiersz kodu tworzy obiecaną wersję całego interfejsu.
Szybszy - Bluebird jest znacznie szybszy niż natywne obietnice w większości środowisk.
Sekwencjonowanie iteracji tablicy asynchronicznej - Promise.mapSeries()
lub Promise.reduce()
pozwala na iterację przez tablicę, wywołując operację asynchroniczną na każdym elemencie, ale sekwencjonując operacje asynchroniczne, aby następowały kolejno po sobie, a nie wszystkie jednocześnie. Możesz to zrobić albo dlatego, że wymaga tego serwer docelowy, albo dlatego, że musisz przekazać jeden wynik do drugiego.
Wielokrotne wypełnianie - jeśli chcesz używać obietnic w starszych wersjach klientów przeglądarki, i tak będziesz potrzebował wielokrotnego wypełniania. Może również uzyskać sprawny wypełniacz. Ponieważ node.js ma obietnice ES6, nie potrzebujesz wielokrotnego wypełniania w node.js, ale możesz to zrobić w przeglądarce. Jeśli kodujesz zarówno serwer, jak i klient node.js, bardzo przydatne może być posiadanie tej samej biblioteki obietnic i funkcji w obu (łatwiejsze udostępnianie kodu, przełączanie kontekstu między środowiskami, stosowanie typowych technik kodowania dla kodu asynchronicznego itp. .).
Inne użyteczne funkcje - Bluebird ma Promise.map()
, Promise.some()
, Promise.any()
, Promise.filter()
, Promise.each()
i Promise.props()
z których wszystkie są czasami przydać. Chociaż operacje te można wykonywać z obietnicami ES6 i dodatkowym kodem, Bluebird jest wyposażony w te operacje, które są już wstępnie zbudowane i przetestowane, więc korzystanie z nich jest prostsze i mniej kodowane.
Wbudowane ostrzeżenia i ślady pełnego stosu - Bluebird ma wiele wbudowanych ostrzeżeń, które ostrzegają o problemach, które prawdopodobnie są błędnym kodem lub błędem. Na przykład, jeśli wywołasz funkcję, która tworzy nową obietnicę w .then()
module obsługi bez zwracania tej obietnicy (w celu powiązania jej z bieżącym łańcuchem obietnic), to w większości przypadków jest to przypadkowy błąd, a Bluebird da ci ostrzeżenie efekt. Inne wbudowane ostrzeżenia Bluebird są opisane tutaj .
Oto więcej szczegółów na te różne tematy:
PromisifyAll
W każdym projekcie node.js natychmiast używam Bluebird wszędzie, ponieważ .promisifyAll()
dużo używam na standardowych modułach node.js, takich jakfs
moduł.
Node.js sam w sobie nie zapewnia obiecującego interfejsu dla wbudowanych modułów, które wykonują asynchroniczne operacje we / wy jak fs
moduł. Tak więc, jeśli chcesz używać obietnic z tymi interfejsami, możesz albo ręcznie kodować opakowanie obietnicy wokół każdej funkcji modułu, której używasz, albo uzyskać bibliotekę, która może to zrobić dla ciebie, lub nie używać obietnic.
Bluebird Promise.promisify()
i Promise.promisifyAll()
zapewniają automatyczne zawijanie asynchronicznych interfejsów API wywołujących node.js w celu zwrócenia obietnic. Jest to niezwykle przydatne i oszczędza czas. Używam tego cały czas.
Oto przykład, jak to działa:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Alternatywą byłoby ręczne utworzenie własnego opakowania obietnicy dla każdego fs
interfejsu API, którego chcesz użyć:
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
});
I musisz to zrobić ręcznie dla każdej funkcji API, której chcesz użyć. To oczywiście nie ma sensu. To jest kod płyty. Równie dobrze możesz uzyskać narzędzie, które wykona to za Ciebie. Bluebird's Promise.promisify()
iPromise.promisifyAll()
są takim narzędziem.
Inne przydatne funkcje
Oto niektóre z funkcji Bluebird, które szczególnie uważam za przydatne (poniżej znajduje się kilka przykładów kodu, w jaki sposób można zapisać kod lub przyspieszyć rozwój):
Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()
Oprócz swojej przydatnej funkcji Promise.map()
obsługuje również opcję współbieżności, która pozwala określić, ile operacji powinno być uruchomionych w tym samym czasie, co jest szczególnie przydatne, gdy masz dużo do zrobienia, ale nie może przytłoczyć niektórych z zewnątrz ratunek.
Niektóre z nich można zarówno nazwać samodzielnymi, jak i użyć w obietnicy, która sama przekształca się w iterowalną, która może zaoszczędzić dużo kodu.
Polyfill
W projekcie przeglądarki, ponieważ generalnie chcesz nadal obsługiwać niektóre przeglądarki, które nie obsługują technologii Promise, i tak będziesz potrzebować wielokrotnego wypełniania. Jeśli używasz również jQuery, czasami możesz po prostu skorzystać z obsługi obietnic wbudowanej w jQuery (chociaż jest to boleśnie niestandardowe pod pewnymi względami, być może naprawione w jQuery 3.0), ale jeśli projekt wymaga jakiejkolwiek znaczącej aktywności asynchronicznej, znajduję rozszerzone funkcje Bluebird są bardzo przydatne.
Szybciej
Warto również zauważyć, że obietnice Bluebird wydają się znacznie szybsze niż obietnice wbudowane w V8. Zobacz ten post, aby uzyskać więcej dyskusji na ten temat.
Big Thing Node.js brakuje
To, co skłoniłoby mnie do rozważenia używania Bluebird mniej w rozwoju node.js, to gdyby node.js został wbudowany w funkcję promisify, abyś mógł zrobić coś takiego:
const fs = requirep('fs');
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Lub po prostu zaoferuj już obiecane metody jako część wbudowanych modułów.
Do tego czasu robię to z Bluebird:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
Wydaje się nieco dziwne, że obsługa obietnic ES6 jest wbudowana w node.js i żaden z wbudowanych modułów nie zwraca obietnic. To musi zostać uporządkowane w node.js. Do tego czasu używam Bluebird do promowania całych bibliotek. Wygląda więc na to, że obietnice są teraz implementowane w node.js w około 20%, ponieważ żaden z wbudowanych modułów nie pozwala na użycie obietnic bez ich ręcznego zawijania.
Przykłady
Oto przykład prostych obietnic vs. obietnicy Bluebird i Promise.map()
do równoległego czytania zestawu plików i powiadamiania o zakończeniu wszystkich danych:
Proste obietnice
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()
iPromise.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
});
Oto przykład zwykłych obietnic vs. obietnic Bluebird i Promise.map()
podczas odczytywania wiązki adresów URL ze zdalnego hosta, na którym możesz odczytać maksymalnie 4 naraz, ale chcesz zachować tyle żądań równolegle, ile jest to dozwolone:
Zwykłe obietnice 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 obiecuje
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
});