Czy nadal istnieją powody, aby korzystać z bibliotek obietnic, takich jak Q lub BlueBird, skoro mamy obietnice ES6? [Zamknięte]

228

Czy po dodaniu natywnej obsługi obietnic przez Node.js nadal istnieją powody do korzystania z bibliotek takich jak Q lub BlueBird?

Na przykład, jeśli zaczynasz nowy projekt i załóżmy, że w tym projekcie nie ma żadnych zależności korzystających z tych bibliotek, czy możemy powiedzieć, że tak naprawdę nie ma już powodów, aby używać takich bibliotek?

Murat Ozgul
źródło
4
natywne obietnice mają bardzo podstawowe cechy. Biblioteki takie jak Q lub Bluebird dodają jeszcze więcej. Jeśli potrzebujesz tych funkcji, skorzystaj z tych bibliotek.
gman
7
Zredagowałem ten tytuł, aby mniej dotyczył „potrzeby”, a bardziej „powodów, dla których warto nadal korzystać z bibliotek obietnic”. Na to pytanie można odpowiedzieć przede wszystkim podając fakty, a nie opinię. Należy go ponownie otworzyć, ponieważ można na nie odpowiedzieć, podając fakty, a nie przede wszystkim opinię. Zobacz odpowiedź poniżej jako przykład.
jfriend00
11
@JaromandaX - Zastanów się nad ponownym otwarciem teraz, gdy tytuł i pytanie zostały poprawione, aby dowiedzieć się więcej o tym, dlaczego należy korzystać z biblioteki obietnic, a nie czy „trzeba” korzystać z biblioteki obietnic. Moim zdaniem na to pytanie można odpowiedzieć, podając fakty, a nie przede wszystkim opinię - zob. Odpowiedź poniżej jako dowód.
jfriend00
6
To pytanie po edycji tytułu i przyjęta odpowiedź nie są oparte na opiniach.
maks
7
Zgoda. To pytanie jest w pełni aktualne w obecnej formie. Nominowałem do ponownego otwarcia.
Jules

Odpowiedzi:

367

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

  1. 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.

  2. Szybszy - Bluebird jest znacznie szybszy niż natywne obietnice w większości środowisk.

  3. 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.

  4. 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. .).

  5. 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.

  6. 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 fsinterfejsu 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
});
jfriend00
źródło
choć pod pewnymi względami jest boleśnie niestandardowy - Twierdzą, że są teraz „Obietnice / kompatybilne z A +” :) - blog.jquery.com/2016/01/14/jquery-3-0-beta-released
thefourtheye
1
@thefourtheye - Tak, wiem, że pracowali nad zgodnością Promise / A + w wersji 3.0. Ale to wciąż jest w fazie beta. Jeśli spełnia warunki obietnicy (zamierzone słowo), może wyeliminować niektóre powody korzystania z zewnętrznej biblioteki obietnic w przeglądarce JS, jeśli już używasz jQuery. Nadal nie będzie miał wszystkich przydatnych funkcji, które robi Bluebird, i byłbym bardzo zaskoczony, jeśli sprosta wydajności Bluebird, więc w niektórych przypadkach nadal jest miejsce na Bluebird obok przyszłego jQuery. W każdym razie pytanie OP wydaje się dotyczyć głównie node.js.
jfriend00
1
Jest trochę literówka w ostatnim przykładzie kodu: return new Promise(function(resolve, rejct). Powinno być:reject
Sebastian Muszyński
7
Node.js faktycznie ma util.promisifyteraz, choć nie ma bezpośredniego promisifyAllodpowiednika.
nyuszika7h
1
@Aurast - Tak, v11 dba o to fs, ale wciąż istnieją inne powody, aby używać Bluebird (moim szczególnym faworytem jest concurrencyopcja Promise.map()), aby nie przytłoczyć usługi docelowej, do której musisz wysłać kilka równoległych żądań. Ponadto wciąż wiele innych nie obiecanych interfejsów do korzystania z promisifyAll z Bluebird. Powoli jednak powody, dla których od razu chwytamy Bluebirda w każdym nowym projekcie, zanikają, ponieważ sam node.js rozwija swoją wbudowaną obsługę obietnic.
jfriend00