Chcę utworzyć stronę wyświetlającą niektóre dane z bazy danych, więc utworzyłem kilka funkcji, które pobierają te dane z mojej bazy danych. Jestem tylko nowicjuszem w Node.js, więc o ile rozumiem, jeśli chcę użyć ich wszystkich na jednej stronie (odpowiedź HTTP), musiałbym je wszystkie zagnieździć:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
var html = "<h1>Demo page</h1>";
getSomeDate(client, function(someData) {
html += "<p>"+ someData +"</p>";
getSomeOtherDate(client, function(someOtherData) {
html += "<p>"+ someOtherData +"</p>";
getMoreData(client, function(moreData) {
html += "<p>"+ moreData +"</p>";
res.write(html);
res.end();
});
});
});
Jeśli takich funkcji jest wiele, zagnieżdżanie staje się problemem .
Czy jest sposób, aby tego uniknąć? Myślę, że ma to związek ze sposobem łączenia wielu funkcji asynchronicznych, co wydaje się być czymś fundamentalnym.
getSomeDate
igetSomeOtherDate
kończy się zmianą wcięcia wielu wierszy, co sprawia, że historia git jest trudniejsza do odczytania (git blame
jest nawet bezużyteczna po tym) i prawdopodobnie robisz błędy, robiąc to ręcznieOdpowiedzi:
Ciekawa obserwacja. Zauważ, że w JavaScript można normalnie zamienić wbudowane anonimowe funkcje zwrotne na nazwane zmienne funkcyjne.
Następujące:
Można przepisać, aby wyglądał mniej więcej tak:
Jeśli jednak nie planujesz ponownie używać logiki wywołań zwrotnych w innych miejscach, często łatwiej jest odczytać wbudowane funkcje anonimowe, jak w twoim przykładzie. Dzięki temu nie będziesz musiał szukać nazwy dla wszystkich wywołań zwrotnych.
Ponadto zauważ, że jak @pst zauważył w komentarzu poniżej, jeśli uzyskujesz dostęp do zmiennych zamykających w ramach funkcji wewnętrznych, powyższe nie byłoby prostym tłumaczeniem. W takich przypadkach korzystanie z wbudowanych funkcji anonimowych jest jeszcze bardziej korzystne.
źródło
getMoreData
utracono dostęp do „res” .someDataParser
faktycznie analizuje WSZYSTKIE dane, ponieważ również wywołujegetMoreData
. W tym sensie nazwa funkcji jest nieprawidłowa i okazuje się, że w rzeczywistości nie usunęliśmy problemu z zagnieżdżeniem.Dobrze, po prostu użyj jednego z tych modułów.
Okaże się to:
Zaangażowany w to:
źródło
W większości zgadzam się z Danielem Vassallo. Jeśli potrafisz rozbić skomplikowaną i głęboko zagnieżdżoną funkcję na osobne nazwane funkcje, to zazwyczaj jest to dobry pomysł. W sytuacjach, w których warto zrobić to w ramach jednej funkcji, możesz użyć jednej z wielu dostępnych bibliotek asynchronicznych node.js. Ludzie wymyślili wiele różnych sposobów rozwiązania tego problemu, więc spójrz na stronę modułów node.js i zobacz, co myślisz.
Sam napisałem do tego moduł o nazwie async.js . Korzystając z tego, powyższy przykład można zaktualizować do:
Jedną z fajnych rzeczy w tym podejściu jest to, że możesz szybko zmienić swój kod, aby równolegle pobierać dane, zmieniając funkcję „szereg” na „równolegle”. Co więcej, async.js będzie również działać w przeglądarce, więc możesz użyć tych samych metod, co w node.js, jeśli napotkasz jakiś skomplikowany kod asynchroniczny.
Mam nadzieję, że to przydatne!
źródło
Możesz użyć tej sztuczki z tablicą zamiast zagnieżdżonych funkcji lub modułu.
O wiele łatwiejsze dla oczu.
Możesz rozszerzyć idiom na równoległe procesy lub nawet równoległe łańcuchy procesów:
źródło
W tym celu bardzo lubię async.js .
Problem rozwiązuje polecenie wodospad:
Przykład
Jeśli chodzi o zmienne req, res, będą one współdzielone w tym samym zakresie, co funkcja (req, res) {}, która obejmuje całe wywołanie async.waterfall.
Nie tylko to, async jest bardzo czysty. Chodzi mi o to, że zmieniam wiele przypadków takich jak ta:
Najpierw:
Następnie do tego:
Następnie do tego:
Pozwala również na bardzo szybkie wywołanie wielu gotowych funkcji przygotowanych do asynchronizacji z pliku util.js. Po prostu połącz to, co chcesz zrobić, upewnij się, że o, cb jest obsługiwane uniwersalnie. To bardzo przyspiesza cały proces kodowania.
źródło
Potrzebujesz trochę cukru syntaktycznego. Sprawdź to:
Całkiem fajnie , prawda? Możesz zauważyć, że html stał się tablicą. Dzieje się tak częściowo dlatego, że ciągi są niezmienne, więc lepiej jest buforować dane wyjściowe w tablicy, niż odrzucać coraz większe ciągi. Innym powodem jest inna ładna składnia z
bind
.Queue
w przykładzie jest tak naprawdę tylko przykładem i razem zpartial
można zaimplementować w następujący sposóbźródło
last
funkcją)obj.email
a następna używa,obj.email
a potem ją usuwa (lub po prostu przypisujenull
).Jestem zakochany w Async.js odkąd go znalazłem. Ma
async.series
funkcję, której możesz użyć, aby uniknąć długiego zagnieżdżania.Dokumentacja:-
seria (zadania, [oddzwonienie])
Uruchom tablicę funkcji szeregowo, z których każda działa po zakończeniu poprzedniej funkcji. […]
Argumenty
tasks
- Tablica funkcji do uruchomienia, każda funkcja otrzymuje wywołanie zwrotne, które musi wywołać po zakończeniu.callback(err, [results])
- Opcjonalne wywołanie zwrotne do uruchomienia po zakończeniu wszystkich funkcji. Ta funkcja pobiera tablicę wszystkich argumentów przekazanych do wywołań zwrotnych używanych w tablicy.Oto jak możemy zastosować to do twojego przykładowego kodu: -
źródło
Najprostszym cukrem syntaktycznym, jaki widziałem, jest obietnica węzłów.
npm zainstaluj obietnicę węzła || klon git https://github.com/kriszyp/node-promise
Używając tego, możesz łączyć metody asynchroniczne jako:
Wartość zwracana każdego z nich jest dostępna jako argument w następnym.
źródło
To, co tam zrobiłeś, polega na wzięciu wzorca asynchronicznego i zastosowaniu go do 3 funkcji wywoływanych po kolei, z których każda czeka na zakończenie poprzedniej przed rozpoczęciem - tj. Uczyniłeś je synchronicznymi . Problem w programowaniu asynchronicznym polega na tym, że możesz mieć kilka funkcji uruchomionych jednocześnie i nie musisz czekać na zakończenie każdej z nich.
jeśli getSomeDate () nie dostarcza niczego do getSomeOtherDate (), co nie dostarcza niczego do getMoreData (), to dlaczego nie wywołujesz ich asynchronicznie, jak na to pozwala js, lub jeśli są współzależne (a nie asynchroniczne), zapisz je jako pojedyncza funkcja?
Nie musisz używać zagnieżdżania do kontrolowania przepływu - na przykład, zakończ każdą funkcję, wywołując wspólną funkcję, która określa, kiedy wszystkie 3 zostały zakończone, a następnie wysyła odpowiedź.
źródło
Załóżmy, że możesz to zrobić:
Wystarczy zaimplementować metodę chain (), aby częściowo stosowała każdą funkcję do następnej i natychmiast wywoływała tylko pierwszą funkcję:
źródło
piekło zwrotne można łatwo uniknąć w czystym javascript z zamknięciem. poniższe rozwiązanie zakłada, że wszystkie wywołania zwrotne są zgodne z sygnaturą funkcji (błąd, dane).
źródło
Niedawno stworzyłem prostszą abstrakcję o nazwie wait.for do wywoływania funkcji asynchronicznych w trybie synchronizacji (opartej na Fibers). Jest na wczesnym etapie, ale działa. Jest pod adresem:
https://github.com/luciotato/waitfor
Korzystając z wait.for , możesz wywołać dowolną standardową funkcję asynchroniczną nodejs, tak jakby to była funkcja synchronizacji.
używając wait. for your code może być:
... lub jeśli chcesz być mniej rozwlekły (a także dodać wykrywanie błędów)
We wszystkich przypadkach getSomeDate , getSomeOtherDate i getMoreData powinny być standardowymi funkcjami asynchronicznymi z ostatnim parametrem wywołanie zwrotne funkcji (err, data)
jak w:
źródło
Aby rozwiązać ten problem, napisałem nodent ( https://npmjs.org/package/nodent ), który niewidocznie wstępnie przetwarza twój JS. Twój przykładowy kod stałby się (tak naprawdę asynchroniczny - przeczytaj dokumentację).
Oczywiście istnieje wiele innych rozwiązań, ale przetwarzanie wstępne ma tę zaletę, że ma niewielkie lub żadne obciążenie w czasie wykonywania, a dzięki obsłudze map źródłowych można je również łatwo debugować.
źródło
Miałem ten sam problem. Widziałem główne biblioteki obsługujące funkcje asynchroniczne w węźle i prezentują one tak nienaturalne tworzenie łańcuchów (musisz użyć trzech lub więcej metod confs itp.), Aby zbudować swój kod.
Spędziłem kilka tygodni na opracowywaniu rozwiązania, które będzie proste i łatwe do odczytania. Proszę, spróbuj EnqJS . Wszystkie opinie będą mile widziane.
Zamiast:
z EnqJS:
Zwróć uwagę, że kod wydaje się być większy niż wcześniej. Ale nie jest zagnieżdżony jak wcześniej. Aby wyglądać bardziej naturalnie, łańcuchy są natychmiast nazywane:
Aby powiedzieć, że wróciło, wewnątrz funkcji, którą wywołujemy:
źródło
Robię to w dość prymitywny, ale skuteczny sposób. Np. Potrzebuję modelu z rodzicami i dziećmi i powiedzmy, że muszę zrobić dla nich oddzielne zapytania:
źródło
Użyj włókien https://github.com/laverdet/node-fibers , dzięki czemu kod asynchroniczny wygląda jak synchroniczny (bez blokowania)
Osobiście używam tego małego opakowania http://alexeypetrushin.github.com/synchronize Próbka kodu z mojego projektu (każda metoda jest właściwie asynchroniczna, działa z plikiem asynchronicznym IO). Biblioteki pomocnicze async-control-flow.
źródło
Task.js oferuje Ci to:
Zamiast tego:
źródło
Po udzieleniu odpowiedzi przez innych stwierdziłeś, że Twoim problemem są zmienne lokalne. Wydaje się, że prostym sposobem na zrobienie tego jest napisanie jednej zewnętrznej funkcji, która będzie zawierała te zmienne lokalne, a następnie użycie zestawu nazwanych funkcji wewnętrznych i dostęp do nich po nazwie. W ten sposób zagnieżdżasz tylko dwie głębokie, niezależnie od tego, ile funkcji musisz połączyć w łańcuch.
Oto próba mojego początkującego wykorzystania
mysql
modułu Node.js z zagnieżdżeniem:Poniżej przedstawiono przepisanie przy użyciu nazwanych funkcji wewnętrznych. Funkcja zewnętrzna
with_connection
może być również używana jako uchwyt na zmienne lokalne. (Tutaj, mam parametrysql
,bindings
,cb
że działają w podobny sposób, ale można po prostu zdefiniować dodatkowe zmienne lokalne wwith_connection
.)Myślałem, że być może byłoby możliwe stworzenie obiektu ze zmiennymi instancji i użycie tych zmiennych instancji jako zamienników zmiennych lokalnych. Ale teraz stwierdzam, że powyższe podejście wykorzystujące funkcje zagnieżdżone i zmienne lokalne jest prostsze i łatwiejsze do zrozumienia. Oduczenie się OO zajmuje trochę czasu :-)
Oto moja poprzednia wersja ze zmiennymi obiektu i instancji.
Okazuje się, że
bind
można to wykorzystać z pewną korzyścią. Pozwala mi pozbyć się nieco brzydkich anonimowych funkcji, które stworzyłem, które niewiele robiły, z wyjątkiem przekazywania samych siebie do wywołania metody. Nie mogłem przekazać metody bezpośrednio, ponieważ wiązałoby się to z niewłaściwą wartościąthis
. Ale za pomocąbind
mogę określić wartośćthis
, której chcę.Oczywiście nic z tego nie jest poprawnym JS z kodowaniem Node.js - spędziłem nad tym tylko kilka godzin. Ale może przy odrobinie dopracowania ta technika może pomóc?
źródło
async.js działa dobrze w tym przypadku. Natknąłem się na ten bardzo przydatny artykuł, który wyjaśnia potrzebę i użycie async.js z przykładami: http://www.sebastianseilund.com/nodejs-async-in-practice
źródło
Jeśli nie chcesz używać „step” lub „seq”, wypróbuj „line”, która jest prostą funkcją ograniczającą zagnieżdżone asynchroniczne wywołanie zwrotne.
https://github.com/kevin0571/node-line
źródło
Asyncawait w stylu C # to inny sposób na zrobienie tego
https://github.com/yortus/asyncawait
źródło
Używając drutu, twój kod wyglądałby tak:
źródło
dla twojej wiedzy rozważ Jazz.js https://github.com/Javanile/Jazz.js/wiki/Script-showcase
źródło