Jak funguje Event Loop v JavaScriptu?

I když to může vyžadovat důkladnou znalost jazyků jako C++ a C, aby bylo možné napsat produkční kód v plném rozsahu, JavaScript lze často napsat pouze se základním porozuměním toho, co lze s jazykem dělat.

Koncepty, jako je předávání zpětných volání funkcím nebo psaní asynchronního kódu, často není tak obtížné implementovat, takže většina vývojářů JavaScriptu se méně zajímá o to, co se děje pod kapotou. Prostě se nestarají o pochopení složitosti, která z nich jazykem hluboce abstrahovala.

Jako vývojář JavaScriptu je stále důležitější porozumět tomu, co se skutečně děje pod kapotou a jak většina těchto složitostí, které jsme od nás abstrahovali, skutečně funguje. Pomáhá nám to činit informovanější rozhodnutí, což může zase výrazně zvýšit výkon našeho kódu.

Tento článek se zaměřuje na jeden z velmi důležitých, ale zřídka pochopených konceptů nebo termínů v JavaScriptu. SMYČKA UDÁLOSTÍ!.

Psaní asynchronního kódu se v JavaScriptu nevyhnete, ale proč kód běžící asynchronně skutečně znamená? tj. Smyčka událostí

Než pochopíme, jak smyčka událostí funguje, musíme nejprve porozumět tomu, co je samotný JavaScript a jak funguje!

Co je JavaScript?

Než budeme pokračovat, rád bych, abychom udělali krok zpět k úplným základům. Co je to vlastně JavaScript? JavaScript bychom mohli definovat jako;

JavaScript je vysokoúrovňový, interpretovaný, jednovláknový neblokující, asynchronní, souběžný jazyk.

Počkej, co to je? Knižní definice? 🤔

Pojďme to rozebrat!

Klíčová slova v tomto článku jsou jednovláknová, neblokující, souběžná a asynchronní.

Jedno vlákno

Vlákno provádění je nejmenší posloupnost naprogramované instrukce, kterou může plánovač řídit nezávisle. Programovací jazyk je jednovláknový, což znamená, že může provádět pouze jeden úkol nebo operaci v jednom okamžiku. To znamená, že by provedl celý proces od začátku do konce, aniž by bylo vlákno přerušeno nebo zastaveno.

Na rozdíl od vícevláknových jazyků, kde může být spuštěno více procesů na několika vláknech současně, aniž by se navzájem blokovaly.

Jak může být JavaScript jednovláknový a zároveň neblokující?

Co ale znamená blokování?

Neblokování

Neexistuje žádná definice blokování; to prostě znamená věci, které na vláknu běží pomalu. Neblokování tedy znamená věci, které nejsou ve vláknu pomalé.

  Jak okamžitě vyzkoušet hry s Google Play na Androidu

Ale počkejte, řekl jsem, že JavaScript běží v jediném vláknu? A také jsem řekl, že neblokuje, což znamená, že úloha běží rychle na zásobníku volání? Ale jak??? A co když spustíme časovače? Smyčky?

Relaxovat! To bychom za chvíli zjistili 😉.

Souběžné

Souběžnost znamená, že kód je spouštěn současně více než jedním vláknem.

Dobře, věci se opravdu dějí podivný Jak nyní může být JavaScript jednovláknový a současně souběžný? tj. provádění jeho kódu s více než jedním vláknem?

Asynchronní

Asynchronní programování znamená, že kód běží ve smyčce událostí. Když dojde k blokování, událost se spustí. Blokovací kód běží dál, aniž by blokoval hlavní prováděcí vlákno. Když blokovací kód skončí, zařadí se do fronty jako výsledek operací blokování a odešle je zpět do zásobníku.

Ale JavaScript má jediné vlákno? Co potom spustí tento blokovací kód a zároveň nechá provést ostatní kódy ve vláknu?

Než budeme pokračovat, udělejme si rekapitulaci výše uvedeného.

  • JavaScript je jednovláknový
  • JavaScript je neblokující, tj. pomalé procesy neblokují jeho spuštění
  • JavaScript je souběžný, tj. provádí svůj kód ve více než jednom vlákně současně
  • JavaScript je asynchronní, tj. spouští blokovací kód někde jinde.

Ale výše uvedené se přesně nesčítá, jak může být jednovláknový jazyk neblokující, souběžný a asynchronní?

Pojďme trochu hlouběji, pojďme dolů k běhovým enginům JavaScriptu, V8, možná má nějaká skrytá vlákna, o kterých nevíme.

Motor V8

Motor V8 je vysoce výkonný modul runtime webového sestavení s otevřeným zdrojovým kódem pro JavaScript napsaný v C++ společností Google. Většina prohlížečů spouští JavaScript pomocí enginu V8 a dokonce jej používá i populární běhové prostředí node js.

V jednoduché angličtině je V8 program C++, který přijímá kód JavaScript, zkompiluje jej a spustí.

V8 dělá dvě hlavní věci;

  • Přidělení paměti haldy
  • Kontext provádění zásobníku volání

Naše podezření bylo bohužel mylné. V8 má pouze jeden zásobník volání, představte si zásobník volání jako vlákno.

Jedno vlákno === jeden zásobník volání === jedno spuštění najednou.

Obrázek – Hacker Noon

Protože V8 má pouze jeden zásobník volání, jak potom JavaScript běží souběžně a asynchronně, aniž by blokoval hlavní prováděcí vlákno?

  7 nejlepších nástrojů a softwaru pro mapování topologie sítě

Zkusme to zjistit napsáním jednoduchého, ale běžného asynchronního kódu a společně ho analyzovat.

JavaScript spouští každý kód řádek po řádku, jeden po druhém (jednovláknové). Jak se očekávalo, první řádek se vytiskne v konzole zde, ale proč je poslední řádek vytištěn před kódem časového limitu? Proč proces provádění nečeká na kód časového limitu (blokování), než spustí poslední řádek?

Zdá se, že nějaké jiné vlákno nám pomohlo tento časový limit provést, protože jsme si docela jisti, že vlákno může v libovolném okamžiku provést pouze jednu jedinou úlohu.

Pojďme se nenápadně podívat do Zdrojový kód V8 na chvíli.

Počkej co??!!! Ve V8 nejsou žádné funkce časovače, žádný DOM? Žádné události? Žádný AJAX?… Jééééé!!!

Události, DOM, časovače atd. nejsou součástí základní implementace JavaScriptu, JavaScript přesně odpovídá specifikacím Ecma Scripts a jeho různé verze jsou často označovány podle jeho specifikací Ecma Scripts (ES X).

Pracovní postup provádění

Události, časovače a požadavky Ajax, to vše jsou poskytovány na straně klienta pomocí prohlížečů a jsou často označovány jako webové rozhraní API. Jsou to ty, které umožňují, aby jednovláknový JavaScript byl neblokující, souběžný a asynchronní! Ale jak?

Existují tři hlavní části pracovního postupu provádění jakéhokoli programu JavaScript, zásobník volání, webové rozhraní API a fronta úloh.

Zásobník hovorů

Zásobník je datová struktura, ve které je poslední přidaný prvek vždy jako první odstraněn ze zásobníku, můžete si to představit jako zásobník štítků, ve kterém lze jako první odstranit pouze první přidaný štítek. Zásobník volání není nic jiného než datová struktura zásobníku, kde se podle toho provádějí úkoly nebo kód.

Podívejme se na níže uvedený příklad;

Zdroj – https://youtu.be/8aGhZQkoFbQ

Když zavoláte funkci printSquare() , je vložena do zásobníku volání, funkce printSquare() volá funkci square(). Funkce square() je vložena do zásobníku a také volá funkci multiply(). Funkce násobení je posunuta na zásobník. Protože funkce multiply vrací a je poslední věcí, která byla odeslána do zásobníku, nejprve se vyřeší její get a odstraní se ze zásobníku, následuje funkce square() a poté funkce printSquare().

Webové rozhraní API

Zde je spuštěn kód, který nezpracovává motor V8, aby „neblokoval“ hlavní spouštěcí vlákno. Když Call Stack narazí na funkci webového API, proces je okamžitě předán Web API, kde je spuštěn a uvolní Call Stack pro provádění dalších operací během jeho provádění.

  11 Co dělat a co nedělat při výběru správného tématu WordPress

Vraťme se k našemu příkladu setTimeout výše;

Když spustíme kód, první řádek console.log se přesune do zásobníku a náš výstup dostaneme téměř okamžitě, po dosažení časového limitu jsou časovače zpracovány prohlížečem a nejsou součástí základní implementace V8, jsou posunuty místo toho do webového rozhraní API, čímž se uvolní zásobník, aby mohl provádět další operace.

Zatímco časový limit stále běží, zásobník přejde na další řadu akcí a spustí poslední soubor console.log, což vysvětluje, proč se nám to vypisuje před výstupem časovače. Jakmile je časovač dokončen, něco se stane. Console.log in a časovač se jako kouzlem znovu objeví v zásobníku hovorů!

Jak?

Smyčka událostí

Než probereme smyčku událostí, projdeme si nejprve funkci fronty úloh.

Vraťme se k našemu příkladu časového limitu, jakmile webové API dokončí provádění úlohy, nejen že ji automaticky přesune zpět do zásobníku volání. Přejde do fronty úkolů.

Fronta je datová struktura, která funguje na principu First in First out, takže když jsou úkoly zařazovány do fronty, dostávají se ven ve stejném pořadí. Úlohy, které byly provedeny pomocí webového rozhraní API, které jsou odesílány do fronty úloh, se poté vraťte do zásobníku volání, aby byl jejich výsledek vytištěn.

Ale počkej. CO JE SAKRA SMYČKA UDÁLOSTÍ???

Zdroj – https://youtu.be/8aGhZQkoFbQ

Smyčka událostí je proces, který čeká, až se zásobník hovorů uvolní, než přenese zpětná volání z fronty úloh do zásobníku hovorů. Jakmile je zásobník prázdný, spustí se smyčka událostí a zkontroluje frontu úloh na dostupná zpětná volání. Pokud nějaké jsou, vloží je do zásobníku hovorů, počká, až se zásobník hovorů znovu uvolní, a zopakuje stejný proces.

Zdroj – https://www.quora.com/How-does-an-event-loop-work/answer/Timothy-Maxwell

Výše uvedený diagram ukazuje základní pracovní postup mezi smyčkou událostí a frontou úloh.

Závěr

I když se jedná o velmi základní úvod, koncept asynchronního programování v JavaScriptu poskytuje dostatečný přehled, aby bylo jasné, co se děje pod kapotou a jak je JavaScript schopen běžet souběžně a asynchronně pouze s jediným vláknem.

JavaScript je vždy na vyžádání, a pokud se chcete naučit, doporučil bych vám, abyste se podívali na toto Kurz Udemy.