Chciałbym to wyjaśnić, ponieważ dokumentacja nie jest na ten temat zbyt jasna;
P1: Czy Promise.all(iterable)
wszystkie obietnice są przetwarzane sekwencyjnie czy równolegle? A dokładniej, jest to odpowiednik wykonywania obietnic łańcuchowych, takich jak
p1.then(p2).then(p3).then(p4).then(p5)....
czy jest jakiś inny rodzaj algorytmu, gdzie wszystko p1
, p2
, p3
, p4
, p5
, itd. są nazywane jednocześnie (równolegle) i wyniki są zwracane najszybciej jak wszystkim determinacji (lub jeden odrzutów)?
P2: Jeśli Promise.all
działa równolegle, czy istnieje wygodny sposób na sekwencyjne uruchamianie iterowalnych?
Uwaga : nie chcę używać Q ani Bluebird, ale wszystkie natywne specyfikacje ES6.
javascript
node.js
promise
es6-promise
Yanick Rochon
źródło
źródło
Promise.all
wykonuje je równolegle.node.js
iio.js
jak tu go używam. Więc tak, implementacja V8, jeśli wolisz.Promise.all
.new Promise(a).then(b); c();
a jest wykonywane najpierw, potem c, potem b. To nie obietnica. Wszystko, co realizuje te obietnice, po prostu obsługuje, gdy się rozwiążą.Odpowiedzi:
Nie, obietnic nie można „zrealizować”. Rozpoczynają swoje zadanie, gdy są tworzone - przedstawiają tylko wyniki - a Ty wykonujesz wszystko równolegle, nawet przed przekazaniem ich do
Promise.all
.Promise.all
czeka tylko na wiele obietnic. Nie ma znaczenia, w jakiej kolejności rozwiązują, ani czy obliczenia są wykonywane równolegle.Jeśli masz już swoje obietnice, nie możesz wiele zrobić, ale
Promise.all([p1, p2, p3, …])
(co nie ma pojęcia kolejności). Ale jeśli masz iterowalne funkcje asynchroniczne, możesz rzeczywiście uruchamiać je sekwencyjnie. Zasadniczo musisz wyjść zdo
a rozwiązaniem jest użycie
Array::reduce
:źródło
then
sekwencja - wartość zwracana jest obietnicą dla ostatniegofn
wyniku i możesz łączyć inne wywołania zwrotne z tym.fn1().then(p2).then(fn3).catch(…
? Nie ma potrzeby używania wyrażenia funkcyjnego.retValFromF1
jest przekazywanep2
, to jest dokładnie to, cop2
robi. Oczywiście, jeśli chcesz zrobić więcej (przekazać dodatkowe zmienne, wywołać wiele funkcji itp.), Musisz użyć wyrażenia funkcyjnego, chociaż zmianap2
w tablicy byłaby łatwiejszaiterable
o[fn1, fn2, fn3, …]
tablicęRównolegle
Zalety: Szybciej. Wszystkie iteracje zostaną wykonane, nawet jeśli jedna się nie powiedzie.
Kolejno
Zalety: Zmienne w pętli mogą być współużytkowane przez każdą iterację. Zachowuje się jak normalny imperatywny kod synchroniczny.
źródło
for (const item of items) await fetchItem(item);
await Promise.all(items.map(async item => { return await fetchItem(item).catch(e => e) }))
async
funkcja jest wywołaniem API i nie chcesz DDOS na serwerze. Masz lepszą kontrolę nad poszczególnymi wynikami i błędami zgłaszanymi podczas wykonywania. Jeszcze lepiej możesz zdecydować, które błędy kontynuować i co przerwać pętlę.Odpowiedź Bergisa sprawiła, że znalazłem się na właściwej drodze, używając Array.reduce.
Jednak, aby faktycznie uzyskać funkcje zwracające moje obietnice wykonania jedna po drugiej, musiałem dodać więcej zagnieżdżenia.
Mój prawdziwy przypadek użycia to tablica plików, które muszę przesłać w kolejności jeden po drugim ze względu na ograniczenia w dół ...
Oto, z czym skończyłem.
Jak sugerują poprzednie odpowiedzi, użyj:
nie czekał na zakończenie przesyłania przed rozpoczęciem kolejnego, a tekst „Wszystkie przesłane pliki” pojawił się jeszcze przed rozpoczęciem przesyłania pierwszego pliku.
Nie jestem pewien, co zrobiłem źle, ale chciałem się podzielić tym, co zadziałało.
Edycja: Odkąd napisałem ten post, teraz rozumiem, dlaczego pierwsza wersja nie działała. then () oczekuje funkcji zwracającej obietnicę. Więc powinieneś podać nazwę funkcji bez nawiasów! Teraz moja funkcja potrzebuje argumentu, więc muszę zawinąć w anonimową funkcję nie pobierającą żadnego argumentu!
źródło
tylko po to, aby rozwinąć odpowiedź @ Bergi (która jest bardzo zwięzła, ale trudna do zrozumienia;)
Ten kod uruchomi każdy element w tablicy i doda następny łańcuch „then chain” na końcu;
mam nadzieję, że to ma sens.
źródło
Możesz również przetwarzać iterowalne sekwencyjnie z funkcją asynchroniczną, używając funkcji rekurencyjnej. Na przykład biorąc pod uwagę tablicę
a
do przetworzenia z funkcją asynchronicznąsomeAsyncFunction()
:źródło
array.prototype.reduce
jest znacznie lepsze pod względem wydajności niż funkcja rekurencyjnareduce
której musisz zbudować całythen()
łańcuch w jednym kroku, a następnie wykonać.Za pomocą async await tablicę obietnic można łatwo wykonać sekwencyjnie:
Uwaga: W powyższej implementacji, jeśli obietnica zostanie odrzucona, reszta nie zostanie wykonana.Jeśli chcesz, aby wszystkie obietnice zostały wykonane, zawiń swoje
await a[i]();
wnętrzetry catch
źródło
równolegle
zobacz ten przykład
uruchamiając kod, będzie konsolidował "CALLED" dla wszystkich sześciu obietnic, a kiedy zostaną rozwiązane, będzie konsolidował co 6 odpowiedzi po przekroczeniu limitu czasu w tym samym czasie
źródło
NodeJS nie uruchamia obietnic równolegle, uruchamia je jednocześnie, ponieważ jest to architektura pojedynczej pętli zdarzeń. Istnieje możliwość równoległego uruchamiania, tworząc nowy proces potomny w celu wykorzystania wielordzeniowego procesora.
Równoległe kontra współbieżne
W rzeczywistości
Promise.all
jest to zestawianie funkcji obietnic w odpowiedniej kolejce (patrz architektura pętli zdarzeń), uruchamianie ich jednocześnie (wywołanie P1, P2, ...), a następnie czekanie na każdy wynik, a następnie rozwiązanie Promise. Wszystko ze wszystkimi obietnicami wyniki. Obietnica. Wszystko zawiedzie przy pierwszej obietnicy, która zawodzi, chyba że sam poradzisz sobie z odrzuceniem.Istnieje zasadnicza różnica między równoległym i współbieżnym, pierwszy z nich będzie wykonywał różne obliczenia w oddzielnym procesie dokładnie w tym samym czasie i będą postępować w tym rytmie, podczas gdy drugi wykona różne obliczenia jeden po drugim, nie czekając na poprzednie obliczenia, aby zakończyć i postępować w tym samym czasie, bez uzależnienia od siebie nawzajem.
Wreszcie, aby odpowiedzieć na twoje pytanie,
Promise.all
nie zostanie wykonany ani równolegle, ani sekwencyjnie, ale równolegle.źródło
Odpowiedź Bergiego pomogła mi uczynić wywołanie synchronicznym.Dodałem poniżej przykład, w którym wywołujemy każdą funkcję po wywołaniu poprzedniej funkcji.
źródło
Możesz to zrobić za pomocą pętli for.
obietnica zwrotu funkcji async
jeśli napiszesz następujący kod, klient zostanie utworzony równolegle
wtedy wszyscy klienci są tworzeni równolegle. ale jeśli chcesz stworzyć klienta sekwencyjnie, powinieneś użyć pętli for
następnie wszyscy klienci są tworzeni sekwencyjnie.
miłego kodowania :)
źródło
async
/await
jest dostępne tylko z transpilerem lub przy użyciu innych silników niż Node. Poza tym naprawdę nie powinieneś mieszaćasync
zyield
. Chociaż zachowują się tak samo z transpilerem ico
naprawdę są zupełnie inne i zwykle nie powinny się wzajemnie zastępować. Powinieneś także wspomnieć o tych ograniczeniach, ponieważ twoja odpowiedź jest myląca dla początkujących programistów.Używałem for do rozwiązywania kolejnych obietnic. Nie jestem pewien, czy to pomaga, ale właśnie to robiłem.
źródło
to może odpowiedzieć na część twojego pytania.
tak, możesz połączyć tablicę funkcji zwracających obietnice w następujący sposób ... (spowoduje to przekazanie wyniku każdej funkcji do następnej). możesz oczywiście edytować go, aby przekazać ten sam argument (lub żadnych argumentów) do każdej funkcji.
źródło
Natknąłem się na tę stronę, próbując rozwiązać problem w NodeJS: ponowny montaż fragmentów plików. Zasadniczo: mam tablicę nazw plików. Muszę dołączyć wszystkie te pliki we właściwej kolejności, aby utworzyć jeden duży plik. Muszę to zrobić asynchronicznie.
Moduł 'fs' węzła zapewnia appendFileSync, ale nie chciałem blokować serwera podczas tej operacji. Chciałem użyć modułu fs.promises i znaleźć sposób na połączenie tych rzeczy razem. Przykłady na tej stronie nie do końca działały dla mnie, ponieważ faktycznie potrzebowałem dwóch operacji: fsPromises.read () do wczytania fragmentu pliku i fsPromises.appendFile () do połączenia z plikiem docelowym. Może gdybym był lepszy z javascriptem, mógłbym sprawić, że poprzednie odpowiedzi działałyby dla mnie. ;-)
Natknąłem się na to ... https://css-tricks.com/why-using-reduce-to-sequably-resolve-promises-works/ ... i udało mi się zhakować razem działające rozwiązanie.
TLDR:
A oto test jednostkowy jaśminu:
Mam nadzieję, że to komuś pomoże.
źródło