Rozważ następujący kod, który odczytuje tablicę plików w sposób szeregowy / sekwencyjny. readFiles
zwraca obietnicę, która jest rozwiązywana dopiero po odczytaniu wszystkich plików po kolei.
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
return new Promise((resolve, reject) =>
var readSequential = function(index) {
if (index >= files.length) {
resolve();
} else {
readFile(files[index]).then(function() {
readSequential(index + 1);
}).catch(reject);
}
};
readSequential(0); // Start!
});
};
Powyższy kod działa, ale nie lubię rekurencji, aby rzeczy występowały sekwencyjnie. Czy istnieje prostszy sposób, aby ten kod można było ponownie napisać, aby nie musiałem używać mojej dziwnej readSequential
funkcji?
Początkowo próbowałem użyć Promise.all
, ale to spowodowało, że wszystkie readFile
połączenia były wykonywane jednocześnie, co nie jest tym, czego chcę:
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
javascript
promise
q
sequential
serial-processing
XåpplI'-I0llwlg'I -
źródło
źródło
readFileSequential()
powrócił już przed wywołaniem następnego (ponieważ jest asynchroniczny, kończy się długo po zwróceniu pierwotnego wywołania funkcji).Odpowiedzi:
Aktualizacja 2017 : użyłbym funkcji asynchronicznej, jeśli środowisko ją obsługuje:
Jeśli chcesz, możesz odroczyć odczytywanie plików, dopóki ich nie potrzebujesz, za pomocą generatora asynchronicznego (jeśli Twoje środowisko je obsługuje):
Aktualizacja: W drugim przemyśleniu - zamiast tego mogę użyć pętli for:
Lub bardziej kompaktowo, z redukcją:
W innych bibliotekach obietnic (takich jak when i Bluebird) masz do tego odpowiednie narzędzia.
Na przykład Bluebird to:
Chociaż tak naprawdę nie ma powodu, aby nie korzystać z asynchronizacji, czekamy na dziś.
źródło
Promise.resolve(Promise.resolve(15))
jest identyczny zPromise.resolve(15)
.Oto jak wolę uruchamiać zadania w szeregu.
źródło
result = result.then(task);
To pytanie jest stare, ale żyjemy w świecie ES6 i funkcjonalnego JavaScript, więc zobaczmy, jak możemy to poprawić.
Ponieważ obietnice wykonują się natychmiast, nie możemy po prostu stworzyć szeregu obietnic, wszystkie uruchomiłyby się równolegle.
Zamiast tego musimy stworzyć tablicę funkcji, która zwróci obietnicę. Każda funkcja zostanie następnie wykonana sekwencyjnie, co rozpocznie obietnicę w środku.
Możemy to rozwiązać na kilka sposobów, ale moim ulubionym sposobem jest użycie
reduce
.To staje się trochę trudne
reduce
w połączeniu z obietnicami, więc podzieliłem jedną wkładkę na kilka mniejszych strawnych ugryzień poniżej.Istotą tej funkcji jest użycie
reduce
zaczynając od wartości początkowejPromise.resolve([])
lub obietnicy zawierającej pustą tablicę.Ta obietnica zostanie następnie przekazana do
reduce
metody aspromise
. Jest to klucz do sekwencyjnego łączenia każdej obietnicy. Następną obietnicą do wykonania jest,func
a kiedythen
pożary wyniki są konkatenowane, a następnie ta obietnica jest zwracana, wykonującreduce
cykl z funkcją następnej obietnicy.Po wykonaniu wszystkich obietnic zwrócona obietnica będzie zawierać tablicę wszystkich wyników każdej obietnicy.
Przykład ES6 (jedna wkładka)
Przykład ES6 (w podziale)
Stosowanie:
źródło
Array.prototype.concat.bind(result)
to część, której mi brakowało, musiałem ręcznie naciskać na wyniki, które działały, ale działały mniej fajnieconsole.log.bind(console)
stwierdzenie z twojego ostatniego przykładu jest zwykle niepotrzebne. Te dni możesz po prostu przejśćconsole.log
. Na przykład.serial(funcs).then(console.log)
. Testowane na obecnych nodejs i Chrome.Promise.resolve([]).then((x) => { const data = mockApi('/data/1'); return Promise.resolve(x.concat(data)) }).then((x) => { const data = mockApi('/data/2'); return Promise.resolve(x.concat(data)); });
Promise.resolve([]).then(x => someApiCall('url1').then(r => x.concat(r))).then(x => someApiCall('url2').then(r => x.concat(r)))
i tak dalejAby to zrobić po prostu w ES6:
źródło
files.forEach
czy pliki są tablicą.for (file of files) {...}
.Promise.resolve()
do tworzenia już rozwiązanej obietnicy w prawdziwym życiu. Dlaczego nie?Promise.resolve()
wydaje się czystszy niżnew Promise(success => success())
.Promise.resolve();
w swoim kodzie.return sequence;
umieściłem mojego w funkcji, więc do rozwiązania na końcu zamiast wstawianiasequence.then(() => { do stuff });
Proste użycie dla standardowej obietnicy Node.js:
AKTUALIZACJA
items-promise to gotowy do użycia pakiet NPM, który robi to samo.
źródło
Musiałem uruchomić wiele sekwencyjnych zadań i wykorzystałem te odpowiedzi, aby stworzyć funkcję, która zająłaby się obsługą każdego sekwencyjnego zadania ...
Funkcja przyjmuje 2 argumenty + 1 opcjonalnie. Pierwszy argument to tablica, nad którą będziemy pracować. Drugi argument to samo zadanie, funkcja, która zwraca obietnicę, następne zadanie zostanie uruchomione dopiero po spełnieniu tej obietnicy. Trzeci argument to wywołanie zwrotne uruchamiane po wykonaniu wszystkich zadań. Jeśli nie zostanie przekazane żadne wywołanie zwrotne, funkcja zwróci utworzoną obietnicę, abyśmy mogli obsłużyć koniec.
Oto przykład użycia:
Mam nadzieję, że zaoszczędzi komuś trochę czasu ...
źródło
Najmilszym rozwiązaniem, które udało mi się wymyślić, były
bluebird
obietnice. Możesz po prostu zrobić,Promise.resolve(files).each(fs.readFileAsync);
które gwarancje, że obietnice są rozwiązywane sekwencyjnie w kolejności.źródło
Promise.each(filtes, fs.readFileAsync)
. Przy okazji, nie musisz tego robić.bind(fs)
?new Array(int)
. Wszystko, co robi, to ustawienielength
pary klucz-wartość, wpływające na liczbę wskaźników używanych podczas iteracji opartej na długości. Ma zero wpływ na faktyczne indeksowanie tablicy lub granice indeksu)Jest to niewielka odmiana innej odpowiedzi powyżej. Korzystanie z natywnych obietnic:
Wyjaśnienie
Jeśli masz te zadania
[t1, t2, t3]
, powyższe jest równoważne zPromise.resolve().then(t1).then(t2).then(t3)
. To jest zachowanie redukcji.Jak używać
Najpierw musisz zbudować listę zadań! Zadanie to funkcja, która nie przyjmuje argumentów. Jeśli potrzebujesz przekazać argumenty do swojej funkcji, użyj
bind
lub innych metod, aby utworzyć zadanie. Na przykład:źródło
Moje preferowane rozwiązanie:
Nie różni się zasadniczo od innych tutaj opublikowanych, ale:
Przykładowe użycie:
Testowane na rozsądnym bieżącym Chrome (v59) i NodeJS (v8.1.2).
źródło
Użyj
Array.prototype.reduce
i pamiętaj, aby zawrzeć obietnice w funkcji, w przeciwnym razie będą już działać!przyjemnie i łatwo ... powinieneś być w stanie ponownie użyć tego samego materiału siewnego do wydajności itp.
Ważne jest, aby chronić się przed pustymi tablicami lub tablicami zawierającymi tylko 1 element podczas korzystania z redukcji , więc ta technika jest najlepszym wyborem:
a następnie nazwij to tak:
źródło
Stworzyłem tę prostą metodę na obiekcie Promise:
Utwórz i dodaj metodę Promise.sequence do obiektu Promise
Stosowanie:
Najlepsze w tym rozszerzeniu obiektu Promise jest to, że jest ono zgodne ze stylem obietnic. Promise.all i Promise.sequence są wywoływane w ten sam sposób, ale mają inną semantykę.
Uwaga
Sekwencyjne uruchamianie obietnic zwykle nie jest bardzo dobrym sposobem korzystania z obietnic. Zwykle lepiej jest użyć Promise.all i pozwolić przeglądarce na uruchomienie kodu tak szybko, jak to możliwe. Istnieją jednak rzeczywiste przypadki użycia - na przykład podczas pisania aplikacji mobilnej za pomocą javascript.
źródło
Promise.all
i swojegoPromise.sequence
. Jeden przyjmuje iterowalną obietnicę, drugi przyjmuje szereg funkcji, które zwracają obietnice.reduce
odpowiedź w odpowiedzi Benjamina jest o wiele prostsza.Możesz użyć tej funkcji, która dostaje listę promiseFactories:
Promise Factory to prosta funkcja, która zwraca obietnicę:
Działa, ponieważ fabryka obietnic nie tworzy obietnicy, dopóki nie zostanie o to poproszona. Działa tak samo jak funkcja ów - w rzeczywistości jest to to samo!
Nie chcesz w ogóle działać w oparciu o szereg obietnic. Zgodnie ze specyfikacją Promise, zaraz po utworzeniu promesy, zaczyna się ona wykonywać. Więc tak naprawdę chcesz szeregu obiecujących fabryk ...
Jeśli chcesz dowiedzieć się więcej o Obietnicach, sprawdź ten link: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
źródło
Moja odpowiedź na podstawie https://stackoverflow.com/a/31070150/7542429 .
To rozwiązanie zwraca wyniki jako tablicę, taką jak Promise.all ().
Stosowanie:
źródło
Bardzo podobała mi się odpowiedź @ joelnet, ale dla mnie ten styl kodowania jest nieco trudny do przyswojenia, więc spędziłem kilka dni, próbując wymyślić, jak wyraziłbym to samo rozwiązanie w bardziej czytelny sposób, a to jest mój weź tylko inną składnię i kilka komentarzy.
źródło
Jak zauważył Bergi, myślę, że najlepszym i przejrzystym rozwiązaniem jest użycie BlueBird.each, kod poniżej:
źródło
Po pierwsze, musisz zrozumieć, że obietnica zostaje wykonana w momencie stworzenia.
Na przykład jeśli masz kod:
Musisz to zmienić na:
Następnie musimy sekwencyjnie łączyć obietnice:
wykonanie
after()
, upewni się, że obietnica zostanie utworzona (i wykonana) tylko wtedy, gdy nadejdzie odpowiedni czas.źródło
Używam następującego kodu, aby rozszerzyć obiekt Promise. Obsługuje odrzucanie obietnic i zwraca szereg wyników
Kod
Przykład
źródło
Jeśli chcesz, możesz użyć funkcji zmniejsz do złożenia sekwencyjnej obietnicy, na przykład:
zawsze będzie działać sekwencyjnie.
źródło
Korzystanie z nowoczesnego ES:
źródło
Z Async / Await (jeśli masz wsparcie ES7)
(musisz użyć
for
pętli, a nieforEach
dlatego, że async / await ma problemy z uruchomieniem w pętli forEach)Bez asynchronizacji / oczekiwania (przy użyciu obietnicy)
źródło
forEach
(zgodnie z tym )Na podstawie tytułu pytania „Rozwiąż obietnice jeden po drugim (tj. W sekwencji)?”, Możemy zrozumieć, że OP jest bardziej zainteresowany sekwencyjną obsługą obietnic przy rozliczeniu niż sekwencyjnymi wezwaniami per se .
Ta odpowiedź jest oferowana:
Jeśli równoczesne połączenia nie są naprawdę pożądane, zapoznaj się z odpowiedzią Benjamina Gruenbauma, która obejmuje kompleksowo połączenia sekwencyjne (itp.).
Jeśli jednak jesteś zainteresowany (dla lepszej wydajności) wzorcami, które pozwalają na jednoczesne połączenia, a następnie sekwencyjną obsługę odpowiedzi, czytaj dalej.
Kuszące jest, aby pomyśleć, że musisz użyć
Promise.all(arr.map(fn)).then(fn)
(jak już wiele razy) lub fantazyjnego cukru z Promise lib (zwłaszcza Bluebird), jednak (dzięki temu artykułowi )arr.map(fn).reduce(fn)
wzór spełni swoje zadanie, z zaletami, które:.then()
używana jest .Oto jest napisane dla
Q
.Uwaga: tylko ten jeden fragment,
Q()
jest specyficzny dla Q. W przypadku jQuery musisz upewnić się, że readFile () zwraca obietnicę jQuery. Z bibliotekami A + obietnice zagraniczne zostaną zasymilowane.Kluczem tutaj jest
sequence
obietnica redukcji , która porządkuje postępowanie zreadFile
obietnicami, ale nie ich tworzenie.A kiedy już to zrozumiesz, może być nieco oszałamiające, gdy zdasz sobie sprawę, że
.map()
scena nie jest tak naprawdę konieczna! Całe zadanie, połączenia równoległe i obsługa szeregowa w prawidłowej kolejności, można wykonaćreduce()
samodzielnie, a także dodatkową zaletą dodatkowej elastyczności w zakresie:Oto
Q
znowu.To jest podstawowy wzór. Jeśli chcesz również dostarczyć dane (np. Pliki lub ich transformację) do dzwoniącego, potrzebujesz łagodnego wariantu.
źródło
sequence.then(() => filePromise)
jest anty-wzór - nie propaguje błędów tak szybko, jak to możliwe (i tworzyunhandledRejection
w bibliotekach, które je obsługują). Raczej powinieneś użyćQ.all([sequence, filePromise])
lub$.when(sequence, filePromise)
. Wprawdzie takie zachowanie może być tym, czego chcesz, gdy chcesz ignorować lub pomijać błędy, ale powinieneś przynajmniej wymienić to jako wadę.unhandledRejection
zdarzenia. W Bluebird możesz obejść ten problem, używającsequence.return(filePromise)
tego samego zachowania, ale dobrze radzi sobie z odrzucaniem. Nie znam żadnego odniesienia, właśnie go wymyśliłem - nie sądzę, by „(anty) wzór” miał jeszcze nazwę.Twoje podejście nie jest złe, ale ma dwa problemy: połyka błędy i stosuje jawną obietnicę konstrukcyjną Antipattern.
Możesz rozwiązać oba te problemy i sprawić, że kod będzie czystszy, nadal stosując tę samą ogólną strategię:
źródło
Jeśli ktoś inny potrzebuje gwarantowanego ŚCIEŻNIE sekwencyjnego sposobu rozwiązywania obietnic podczas wykonywania operacji CRUD, możesz również użyć następującego kodu jako podstawy.
Tak długo, jak dodasz „return” przed wywołaniem każdej funkcji, opisując obietnicę, i wykorzystaj ten przykład jako podstawę do następnego wywołania funkcji .then () KONSEKWENTNIE rozpocznie się po zakończeniu poprzedniej:
źródło
Do sekwencji obietnic można zastosować metodę push i pop tablic. Możesz także przekazywać nowe obietnice, gdy potrzebujesz dodatkowych danych. To jest kod, którego użyję w React Infinite loader do ładowania sekwencji stron.
źródło
Większość odpowiedzi nie zawiera wyników WSZYSTKICH obietnic indywidualnie, więc w przypadku, gdy ktoś szuka tego konkretnego zachowania, jest to możliwe rozwiązanie za pomocą rekurencji.
Jest zgodny ze stylem
Promise.all
:Zwraca tablicę wyników w
.then()
wywołaniu zwrotnym.Jeśli jakaś obietnica się nie powiedzie, zostanie natychmiast zwrócona w
.catch()
wywołaniu zwrotnym.Uwaga na temat
tasks
deklaracji tablicowej :W takim przypadku nie jest możliwe użycie następującej notacji, takiej jak
Promise.all
:I musimy użyć:
Powodem jest to, że JavaScript zaczyna wykonywać obietnicę natychmiast po jej ogłoszeniu. Jeśli użyjemy takich metod
Promise.all
, po prostu sprawdza, czy stan wszystkich z nich jestfulfilled
lubrejected
, ale nie uruchamia samej ekekcji. Używając() => promise()
tego zatrzymujemy wykonywanie aż do jego wywołania.źródło
Tutaj kluczem jest sposób wywołania funkcji uśpienia. Musisz przekazać tablicę funkcji, która sama zwraca obietnicę zamiast tablicy obietnic.
źródło
Ma to na celu wyjaśnienie, w jaki sposób przetwarzać sekwencję obietnic w bardziej ogólny sposób, obsługując sekwencje dynamiczne / nieskończone, w oparciu o implementację spex.sequence :
Nie tylko to rozwiązanie będzie działać z sekwencjami dowolnej wielkości, ale możesz łatwo dodawać do niego ograniczanie danych i równoważenie obciążenia .
źródło