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
@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"!
No w końcu coś o JavaScript, jedynym słusznym (i potrzebnym ludzkości) języku programowania.
Panowie, proszę wincyj takich dyskusji.
@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);
```
Zaloguj się aby komentować