async await - nie będę Wam jeszcze wyjaśniał co to jest, ale wyjaśnię jak sobie z tym radzić (chyba)

No więc ten post jest w pewien sposób eksperymentalny - nie mam mianowicie pojęcia, czy to co piszę jest prawdą i nie chce mi się szukać informacji i źródeł na ten temat.

Powód jest prozaiczny - chciałbym sprawdzić tolerancję odbiorców na drobne nieścisłości, oraz czy są wśród nas eksperci, którzy mogliby mnie poprawić. Jeśli będę zawsze chciał potwierdzać u źródeł czy dobrze mi się wydaje, to odcinki nie będą wychodziły codziennie, tylko co miesiąc. Pożyjemy, zobaczymy. Może mam dostatecznie dobrą intuicję, żeby nie wprowadzać Was za często w błąd.

W szczególności - pojęcia "wątków", "procesów", "korutyn", "przerwań", "rdzeni" zostanie w tym poście spłaszczone do abstrakcyjnej koncepcji "procesów wielowątkowych i jednowątkowych". Moje stosowanie zwrotu "wątek" nie ma na myśli pojęcia "thread", a abstrakcyjny "wątek który wykonuje się niezależnie na osi czasu". A purystom chuj w dupę.

Zacznijmy od programów "jednowątkowych". Jak mogliście do tej pory zakładać czytając wpis o process.js: https://www.hejto.pl/wpis/tlumacze-jak-lajko-sortuje-posty-zeby-stworzyc-inaczej-posegregowana-liste-to-na, wątek działa tak, że wykonuje kolejno komendy linijka po linijce. A to `console.log` coś wypisze, a to `let twoj_stary = 'najebany'` przypisze jakąś wartość do zmiennej. Dzień jak co dzień.

No i wszystko byłoby spoko, tylko w pewnym momencie się okazało, że właściwie to nie za bardzo się da zrobić tak żeby wątek (czyli lista instrukcji) wykonywał się szybciej.

No to wymyślono zamiast tego, że można zrobić kilka wątków i one będą się wykonywały w tym samym czasie. Pomyśl o tym tak - dziewięć kobiet nie urodzi dziecka w miesiąc, ale jeśli każda zajdzie w ciążę to urodzą dziewiątkę dzeci w dziewięć miesięcy. Średnio 1 dziecko/miesiąc. Pointa tego żartu to właśnie wyjaśnienie czym jest programowanie wielowątkowe i co może nam dać oraz jakie ma ograniczenia.

Zamieniając życie na programowanie, zamiast rodzenia dziecka przez kobiety porozmawiamy o wykonywaniu zapytań HTTP. Zapytanie HTTP to to co robi przeglądarka jak wpiszesz `hejto.pl/najnowsze/strona/1`. Ja będę to symulował pisząc `axios.get(...)` - axios to tak zwana biblioteka (ta służy do robienia zapytań HTTP), ale nie potrzebujesz jeszcze szczegółowej wiedzy w tym zakresie.

Z zapytaniami HTTP jest trochę jak z rodzeniem dziecka - czasem długo trwają. Myślę, że zauważył to każdy, kto odwiedza regularnie #hejto.

Więc programując w JavaScript używa się do tworzenia takiego "wątku" funkcji z napisem `async`. Z tego co rozumiem zwraca ona `Promise`. `Promise` będzie się wykonywał niezależnie od tego, co zrobimy w wątku który go stworzył (np. przez wywołanie funkcji). Jeśli w funkcji głównej wywołacie `sleep`, czyli nie robienie niczego na np. 20 sekund, to taki request (czyli ZAPYTANIE HTTP ale po angielsku i widzę że notorycznie wtrącam to słówko i jestem już zmęczony przepisywaniem) pewnie zdąży się wykonać i kolejne funkcje korzystające z odpowiedzi (response) na zapytanie (request) będą działały poprawnie.

Problem z JavaScriptem jest taki, że jeśli funkcja jest zadeklarowana jako `async` to po prostu będzie się wykonywała w tle (osobnym wątku - tzw. asynchronicznie) i trzeba robić specjalne sztuczki, żeby poczekać na jej wynik. No i `axios.get(...)` właśnie taką funkcją jest xD

Na przykładzie - załóżmy że przypiszemy `Promise` zwrócony przy wykonywaniu zapytania to zmiennej `p` (pewnie coś w stylu `let p = axios.get(...)`). Proces wysyłania zapytania i odbierania odpowiedzi odbywa się "w tle". `Promise` (czyli typ zmiennej `p`) pozwala nam na poczekanie na odpowiedź na dwa sposoby;
- napisanie `await p()`, żeby poczekać aż się wykona przed wykonaniem kolejnych instrukcji
- napisanie `p().then()`, żeby zrobić coś po tym jak się wykona

Oprócz `then` można zrobić też `catch`, żeby sobie np. wypisać błąd jak się wydarzy (w przypadku hejto - czasem jest to błąd z "klasy 500" czyli błędów serwera typu wszelakiego. O tym pogadamy za dość długi czas). W przypadku `await` wydaje mi się, że używa się bloków `try { ... } catch { ... }`, natomiast sugeruję po prostu robić tylko `.then(...).catch(...)` podczas "prototypowania".

No i tutaj kolega @VonTrupka słusznie przewidział w jednym z historycznych wpisów, że wybór nodejs był w pewien sposób pójściem na kompromis xD Otóż w wypadku `nodejs` nie można zrobić `await` w głównym wątku, jak początkowo chciałem. Wydawało mi się, że powinno się dać zrobić `let resp = await axios.get(...)`. No ale się nie da. Zajęło mi ze trzy godziny pogodzenie się z wrzuceniem tego co faktycznie chcę żeby się wykonało w ramach tego "skryptu" (czyli małego programu) do funkcji `main` i użycie `main().then(...).catch(...)` w głównym wątku.

Żeby zrozumieć czemu ten cały cyrk ma sens trzeba wiedzieć jak działa nodejs na dość specjalistycznym poziomie. Ja mam swoją teorię spiskową że jest jednowątkowe i tylko "udaje" wielowątkowość. Ale nie wiem czy to prawda, a Ty ani nie musisz tego wiedzieć ani tym bardziej rozumieć o czym mowa.

Moje eksperymenty doprowadziły mnie do przeczucia, że czekanie na to aż funkcja kryjąca się pod `p` typu `Promise` się wykona, to zastosowanie formatu - `p().then(...).catch(...)` - w ten sposób możemy w `then` po prostu przypisać zwracaną wartość do jakiejś zmiennej i udawać że cały program jest jednowątkowy.

Jeśli udało Wam się zrozumieć co się dzieje z mojego opisu - gratuluję. Trochę Wam, trochę sobie, bo wydaje mi się że koncept "rzeczy dziejących się jednocześnie" i czekania na nie żeby nie działy się jednocześnie tylko po kolei nie jest super intuicyjne.

W nagrodę kolejny wpis będzie łatwy - o tym co zwraca hejto i jak wyciągnąłem z tego treść Waszych wpisów.

#lajko <--- mój tag (przesunąłem na pierwsze miejsce i posortowałem resztę w takiej kolejności, w jakiej uważam że trafienie do ich odbiorców jest dla mnie istotne, sorki memorki jeśli kogoś uraziłem)

#programowanie #tworczoscwlasna #gruparatowaniapoziomu #javascript
666

@wombatDaiquiri panie, praktyka tak, ale w parze z rownolegla nauka. Inaczej bedziesz uskutecznial cargo cult programming czyli nigdy nie zrozumiesz jak dziala kod i czego sie spodziewac.


w wypadku `nodejs` nie można zrobić `await` w głównym wątku, jak początkowo chciałem

oczywiscie ze mozna. Od node v15+ jest to mozliwe LINK


Kolejna rzecz to jednowatkowosc node.js. Wszyscy tak pisza a prawda jest taka ze node.js od samego poczatku jest wielowatkowy out of the box. wszelkie operacje i/o (operacje sieciowe, czytanie dysku, itp) przetwarzane sa w osobnym watku (czy tam procesie).

Przykladowo:


console log('xxxxx');

const data = axios.get(....); <- node.js wlasnie tworzy nowy watek i kiedy zakonczy pobieranie danych zwroci wartosc. Wykonywanie kodu leci dalej i nie czeka na axios

const xx = 'ala ma kota' <- kod wykonywany w glownym watku rownoczesniej z pracujacym axiosem w innym watku

logger('wiadomosc'');

console.log( await data); <--- glowny watek wroci tutaj do wykonywania kodu w momencie kiedy watek obslugujacy polaczenie sieciowe zwroci dane


Do ksiazek a nie "zobaczymy co sie stanie"!

Flaaj

@666 node działa na jednym wątku.

```const xx = 'ala ma kota' <- kod wykonywany w glownym watku rownoczesniej z pracujacym axiosem w innym watku```

nie wykonuje się równocześnie, a później (tylko że, wbrew temu co myśli dużo ludzi, obecne wersje node są bardzo szybkie) , a raczej wtedy, kiedy nadejdzie jego kolej wyznaczona przez event loop. Synchroniczny kod ma pierwszenstwo, po wykonaniu całego zakolejkowanego synchronicznego kodu, watek jest zwalniany i asynchroniczne funkcje mogą się wykonać.


Jeśli chcesz "prawdziwą" wielowątkowość, to masz od tego Worker Threads.

wombatDaiquiri

@666 


Inaczej bedziesz uskutecznial cargo cult programming


No i zajebiście ( ͡o ͜ʖ ͡o) możesz mnie przepytać ze specyfikacji Golanga na wyrywki xD to jest projekt po godzinach, bez napinki, żeby pokazać ludziom że programowanie jest w sumie proste jak chcesz tylko "coś uzyskać". Nie zamierzam się kreować na eksperta jsa, to mój pierwszy projekt od czasów jQuery xD


Do tematu: "change file extension to mjs" sorry, ale podziękuję za takie odczarowanie. Z artykułu brzmi jak syntactic sugar.

wombatDaiquiri

@Flaaj no dobra, a jak wątek robi "await" to nie powinien wtedy "się zatrzymać" żeby mógł się odpalić drugi wątek (asynchroniczna funkcja na której ten robi await)?

Flaaj

@wombatDaiquiri i mean, JS w przeglądarce jak i Node działają na runtimie, który w środku wszystko procesuje. Wątek się nie zatrzymuje, tylko fragment kodu asynchronicznego jest "zakolejkowany" na póżniej, i ten runtime dalej sobie procesuje synchroniczny kod i co jakiś czas przelatuje po wszystkich Promise'ach i patrzy czy już są resolved albo rejected. Jesli jest resolve/reject, to odpala asynchroniczny kod, który ma się wykonać po reject/resolve tego promise'a.


Oczywiście to nie jest takie proste jak ja to opisałem, ale w duuuuuużym przybliżeniu to tak działa.


Ogólnie feeling z programowania jest taki, jakby rzeczywiście była wielowątkowość, jednak jak zbadasz użycie procesora to raczej nie zauwazysz aktywności więcej, niż jednego procesu, o ile nie używasz Worker Threadów

666

@Flaaj nie dziala na 1 watku. Tak jak pisalem, out of the box masz thread pool = 4. To co jest jednowatkowe to event loop i dlatego nie warto go blokowac (mozna ofc) bo wszystko siada.


Jak robisz polaczenie sieciowe to jego obsluga "zrzucana" jest na niskopoziomowa biblio libuv ktora to ma do dyspozycji 4 thready defaultowo. Jesli twoj procesor ma wiecej niz 1 rdzen to operacje wykonywane przez libuv beda rownolegle.


https://www.freecodecamp.org/news/node-js-what-when-where-why-how-ab8424886e2/

To accommodate the single-threaded event loop, Node.js uses the libuv library, which, in turn, uses a fixed-sized thread pool that handles the execution of some of the non-blocking asynchronous I/O operations in parallel. The main thread call functions post tasks to the shared task queue, which threads in the thread pool pull and execute.


wbrew temu co myśli dużo ludzi, obecne wersje node są bardzo szybkie

Od wersji v10, kazda kolejna robi sie wolniejsza i dlatego powoli ewakuuje sie z tego - node.js zamiast przyspieszac zwalnia.

Flaaj

@666 dobra, o tym to nie wiedziałem

wombatDaiquiri

@666 @Flaaj dzięki, mam nadzieję, że wartościowy content będzie właśnie w komentarzach jak mi będzie brakowało wiedzy

rakokuc

No w końcu coś o JavaScript, jedynym słusznym (i potrzebnym ludzkości) języku programowania.


Panowie, proszę wincyj takich dyskusji.

wombatDaiquiri

@rakokuc xd wypierdolę to tak szybko jak tylko będę mógł. język dla leniwych ludzi super.

VonTrupka

@rakokuc @wombatDaiquiri ja przyjmę po stufce od obydwu stron i możecie rzucać w siebie ciętymi ripostami [̲̅$̲̅(̲̅ ͡° ͜ʖ ͡°̲̅)̲̅$̲̅]

ja tylko będę wystawiał noty końcowe za styl i polot (⌒ ͜ʖ⌒)

Meverth

@wombatDaiquiri należałoby jeszcze dorzucić do tego, że część się wykonuje na wątkach react, część jest oddelegowana do przeglądarki.

@Flaaj, @666, poprawcie mnie, proszę.

```

console.log(0);

const defferred = async function timeouted2() { await new Promise(resolve => setTimeout(() => { console.log(1); resolve(); }, 5)); };

setTimeout(() => {console.log(2);}, 5);

new Promise(resolve => setTimeout(() => { console.log(3); resolve(); }, 5)).then(() => console.log(4));

defferred().then(() => {setTimeout(() => {console.log(6); }, 5);});

setTimeout(function timeouted() { console.log(7) }, 5);

console.log(8);

```

wombatDaiquiri

@Meverth 


należałoby


Nie przesadzajmy ( ͡° ͜ʖ ͡°)


część jest oddelegowana do przeglądarki


Ale w jaki sposób to się dzieje jak ja to odpalam przez nodejs z terminala? AFAIK to nie odpala headless chrome w tle? Coś tu chyba zmyślasz.

Meverth

@wombatDaiquiri idzie przez API przeglądarki. Nie wiem, czy wiesz, ale JS jest wykonywany w przeglądarce


Nie wiem, czy zmyślam, takie tłumaczenie dostałem na rekrutacji. Bezmyślnie powtarzam, szukam potwierdzenia u mądrzejszych, którzy mają z JS do czynienia na co dzień.

Zaloguj się aby komentować