[{"data":1,"prerenderedAt":54},["ShallowReactive",2],{"post-en-when-we-say-event-loop":3,"translation-en-when-we-say-event-loop":53},["Reactive",4],{"id":5,"date":6,"date_gmt":7,"guid":8,"modified":7,"modified_gmt":7,"slug":10,"status":11,"type":12,"link":9,"title":13,"content":15,"excerpt":18,"author":20,"featured_media":20,"comment_status":21,"ping_status":21,"sticky":17,"template":19,"format":22,"meta":23,"categories":24,"tags":26,"tagsResolved":27,"translations":40},10542940,"28. 09. 2026","2026-09-28T00:00:00",{"rendered":9},"local:kdyz-se-rekne-event-loop","kdyz-se-rekne-event-loop","publish","post",{"rendered":14},"Když se řekne Event Loop",{"rendered":16,"protected":17},"\u003Cp>Práce softwarového vývojáře vyžaduje, abychom se učili novým věcem a sledovali trendy v oboru. Ať už z důvodu zlepšení si pozice na pracovním trhu nebo jen pro zvýšení svých znalostí a produktivity. V posledních dvou letech se k tomu, co musíme sledovat, nejdříve objevily první náznaky umělé inteligence v podobě různých AI asistentů, co-pilotů a integrací ChatGPT, které umí nahradit běžné a rutinní procesy, ale i generovat řešení složitějších problémů. Dnes, v době psaní tohoto textu, dokážou přední jazykové modely na základě jednoho promptu v Claude Code Fable nebo ChatGPT Astra vygenerovat celý projekt, který lze deploynout, otestovat a spustit. Od softwaru, medicíny až po copywriting a překládání, nyní čelí v těchto oborech profesionálové a profesionálky vlnám propouštění. O této tzv. \u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTechnological_unemployment\">technologické nezaměstnanosti\u003C\u002Fa> se už nemluví pouze na akademické půdě. Začalo být tématem a každodenní realitou bílolímečkových oborů viz \u003Ca href=\"https:\u002F\u002Fwww.technologyreview.com\u002F2024\u002F01\u002F27\u002F1087041\u002Ftechnological-unemployment-elon-musk-jobs-ai\u002F\">zde\u003C\u002Fa> nebo \u003Ca href=\"https:\u002F\u002Fwww.ilo.org\u002Fresource\u002Farticle\u002Fminimizing-negative-effects-ai-induced-technological-unemployment\">zde\u003C\u002Fa>. Není to poprvé, ani naposled, kdy pokroky v technologiích mnoho zaměstnání a oborů dělá irelevantní, a naopak vytváří nové. V takových případech dochází k oddělování \u003Ca href=\"https:\u002F\u002Fdictionary.cambridge.org\u002Fdictionary\u002Fenglish\u002Fseparate-the-wheat-from-the-chaff\">zrna od plev\u003C\u002Fa> a je legitimní se ptát, kdo zůstane v této AI-powered tržní ekonomice pro korporace užitečný?\u003C\u002Fp>\n\u003Cp>Juniorní pozice se zdají být prvním a přirozeným terčem. Povrchní znalosti, nerozvinutá schopnost vidět technické problémy v širším kontextu technických, ale i byznysových požadavů. Tím AI přestává trpět, kdežto juniorní pozice jsou tímto známé.\u003C\u002Fp>\n\u003Cp>Podívám-li se na svůj obor front-endu a back-endových serverových runtimů node\u002Fdeno\u002Fbun, hlubší znalost Event Loopu mi přijde, že je jedna z několika tzv. „hard skills“, které v prostředí Javascriptu oddělují juniorního kodéra od seniorního softwarového inženýra. Pochopením Event Loopu dokáže člověk odpovědět na několik důležitých otázek, například: jak je možné, že Javascript je single-threaded programovací jazyk, přesto v něm děláme několik věcí naráz pomocí asynchronních operací? O Event Loopu lze psát leccos. Mé ambice jsou skromné. Podívám se na Event Loop takovým způsobem, abych dokázal vysvětlit jednu z nejběžnějších otázek na pracovním pohovoru na pozici front-end vývojářem: v jakém pořadí se provedou následující operace a proč:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-js\">setTimeout(() =&gt; {\n  console.log('hey, from set timeout');\n}, 0);\n\nnew Promise((resolve) =&gt; resolve('ahoy from promise')).then((v) =&gt;\n  console.log(v)\n);\n\nconsole.log('hello from console log');\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>Technický úvod do problému\u003C\u002Fh2>\n\u003Cp>Javascript je \u003Cstrong>single-threaded\u003C\u002Fstrong>. Pokud nepočítáme \u003Ca href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FWeb_Workers_API\">Web Workers API\u003C\u002Fa>, znamená to, že Javascript umí zpracovávat v jeden okamžik pouze jedinou operaci. Javascript je navržen tak, že zpracovává operace sekvenčně metodou LIFO (Last-In, First Out), a dokud operace není dokončena, blokuje všechny následující operace. Za normálních okolností by například po kliknutí na tlačítko blokoval jeden pomalý GET request na vzdálené API veškerou uživatelskou interakci; celé uživatelské rozhraní prohlížeče by zamrzlo, dokud by se síťový požadavek (network request) nevyřídil. Když v devadesátých letech navrhl Brendan Eich Javascript, bylo tehdy v Javascriptu zpracování operací synchronní, s omezenými možnostmi zpracování událostí, které vyvolala uživatelská interakce s prohlížečem. Stálo by za historickou sondu, proč Eich takto Javascript navrhl. V současné době ale víme, že na Javascriptu založené webové stránky (tzn. celý web) se takto nechovají. Uživatel může interagovat s více částmi webové stránky najednou. Jak toho může single-threaded programovací jazyk dosáhnout?\u003C\u002Fp>\n\u003Ch2>Enter Javascript runtime\u003C\u002Fh2>\n\u003Cp>Když se řekne Javascript, musíme si uvědomit, že se jedná o \u003Cstrong>kolekci knihoven a komponent\u003C\u002Fstrong>. Teprve jejich propojením získáme funkční celek – \u003Cstrong>runtime\u003C\u002Fstrong>, tedy prostředí, ve kterém běží naše aplikace. V současné době máme na výběr ze dvou hlavních typů runtime. Buď se jedná o runtime webového prohlížeče, nebo o runtime jedné z několika serverových implementací, například Node.js, Deno, či nejnovější Bun. Přestože runtime v prohlížečích a na serverech má společné prvky, najdeme v nich i důležité rozdíly. Ty vyplývají ze zaměření daného typu runtime. Webové prohlížeče se orientují na práci s DOM (\u003Ccode>window\u003C\u002Fcode>, \u003Ccode>document\u003C\u002Fcode>), uživatelské interakce (\u003Ccode>addEventListener\u003C\u002Fcode>), síťové operace pomocí \u003Ccode>fetch\u003C\u002Fcode> nebo \u003Ccode>XMLHttpRequest\u003C\u002Fcode>, lokální úložiště v prohlížeči (\u003Ccode>localStorage\u003C\u002Fcode>, \u003Ccode>sessionStorage\u003C\u002Fcode>) aj. Zatímco runtime Node.js nebo Bun se orientuje na prostředí serverových aplikací, práci se souborovým systémem, databázemi, nebo low-level operace nad operačním systémem. \u003Cstrong>Oba typy runtime ale implementují Event Loop\u003C\u002Fstrong>. Pro zjednodušení se dále budu zajímat pouze o runtime webových prohlížečů.\u003C\u002Fp>\n\u003Ch2>Runtime webových prohlížečů\u003C\u002Fh2>\n\u003Cp>Javascriptový runtime webových prohlížečů se skládá z následujících částí:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Javascript Engine\u003C\u002Fli>\n\u003Cli>Web APIs\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Event Loop\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>Task queue a Microtask queue\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Event Loop je pouze jedna z komponent a přichází do hry pouze v případě zpracování asynchronního kódu. Pokud v našem Javascriptovém programu používáme pouze synchronní operace, Event Loop pak nebude nikdy využitý. Přesto Event Loop patří mezi hlavní charakteristiky Javascriptu a díky němu jsou současné moderní webové aplikace plné asynchronních funkcionalit. Pro pochopení Event Loopu a zpracování asynchronního kódu v Javascriptu vůbec, je dobré si připomenout, \u003Cstrong>jak se chová Javascript při plně synchronních operacích\u003C\u002Fstrong>. Na to je dobré pak pochopit fungování Javascript enginu.\u003C\u002Fp>\n\u003Ch3>Javascript engine\u003C\u002Fh3>\n\u003Cp>Pomyslným srdcem Javascriptu je engine. Hlavní funkce JavaScript enginu jsou parsování, interpretace a exekuce kódu. Parsingem kódu myslíme \u003Cstrong>převod zdrojového kódu JavaScriptu na tzv. abstraktní syntaktický strom (AST)\u003C\u002Fstrong>, což je struktura, kterou engine dokáže zpracovat. Interpretací myslíme to, že \u003Cstrong>většina moderních enginů nejdříve kód interpretuje a poté kompiluje do strojového kódu (just-in-time compilation – JIT)\u003C\u002Fstrong>. Exekucí kódu pak myslíme, že \u003Cstrong>po překladu do strojového kódu začne engine provádět instrukce a zajistí běh aplikace\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>Příklady populárních JavaScriptových enginů:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>V8: Používá jej Google Chrome a Node.js. Je známý svou rychlostí a efektivitou.\u003C\u002Fli>\n\u003Cli>SpiderMonkey: První JavaScriptový engine, vyvinutý společností Netscape, dnes používán ve Firefoxu.\u003C\u002Fli>\n\u003Cli>JavaScriptCore (aka Nitro): Používá jej Safari a další produkty od Apple.\u003C\u002Fli>\n\u003Cli>Chakra: Používal jej Microsoft Edge (legacy), nyní je nahrazen enginem V8 v novém Edge (Chromium).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>JavaScript engine dále poskytuje dvě paměťové struktury: \u003Cstrong>heap\u003C\u002Fstrong> a \u003Cstrong>call stack\u003C\u002Fstrong>. Jejich rozdíly a fungování by si opět zasloužily samostatný článek na téma Memory managementu v Javascriptu. Základní definici si přečtětě na Wikipedii nebo vypromptujte s Chat GPT.\u003C\u002Fp>\n\u003Cp>Co je pro pochopení Event Loopu důležité je, že \u003Cstrong>Javascript Engine jako takový neimplementuje zpracování asynchronního kódu\u003C\u002Fstrong>. Veškerou asynchronicitu Javascriptu je nutné implementovat jinde. Kde jinde? To má právě na starosti Javascript Runtime, který k Javascript enginu přidává Web APIs (jsme v browseru), Event loop a čekací fronty Task queue a Microtask queue. Fun fact: pokud by Javascript byl čistě synchronní programovací jazyk, vystačil by si pouze s heap a call stackem a nepotřeboval by Event Loop. Ale to předbíhám.\u003C\u002Fp>\n\u003Ch3>Heap\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>Heap\u003C\u002Fstrong> co se týče pochopení Event Loopu není až tak důležitý. Heap je paměťová struktura, kde Javascript Engine ukládá komplexní datové typy (Object, Array, Function, Map, Set, WeakMap, WeakSet). Velikost heapu je dána velikostí paměti RAM. Narozdíl od call stacku není heap organizován sekvenčně, je to nelineární paměť. Do heapu se ukládají data, jejichž typ a velikost neznáme během kompilace a mohou se měnit během runtime (přiřazení jiných hodnot properties objektů, vznik nových properties, nových objektů apod.), takže heap je dynamická paměť. Heap je technicky vzato implementace běžného návrhového vzoru dynamické alokace paměti (dynamic memory allocation pattern), což znamená, že vyžaduje Garbage Collector, který čistí alokovanou paměť během runtime podle toho, zda nějaký kus programu vyžaduje (má reference v call stacku) na objekty alokované v heap. Pokud ne, Garbage Collector za normálních okolností pročistí heap a uvolní paměť. Garbage Collector ale není 100% přesný, tudíž i přes jeho existenci stále může dojít k únikům paměti (memory leaks).\u003C\u002Fp>\n\u003Ch3>Call Stack\u003C\u002Fh3>\n\u003Cp>Na druhé straně je \u003Cstrong>call stack\u003C\u002Fstrong> jednodušší statická paměť a mnohem zajímavější pro vysvětlení Event Loopu, protože má co dočinění s pořadím toho, jak se v Javascriptu vykonává kód. Javascript engine pracuje s call stackem pouze synchronním způsobem krok za krokem. Po technické stránce je call stack implementací klasického \u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FStack_(abstract_data_type)\">návrhového vzoru Zásobníku (Stacku)\u003C\u002Fa>, kdy poslední přidaný element do paměti je zpracováván jako první (Last In, First Out, LIFO). A co přesně je v call stacku uloženo a určuje pořadí \u003Cstrong>čeho\u003C\u002Fstrong> přesně? Pro zjednodušení je možné si představit Javascriptový program jako kolekce funkcí a proměnných a call stack zajišťuje, aby naše funkce byly volány a proměnné byly deklarovány ve správném pořadí a ve správný čas.\u003C\u002Fp>\n\u003Cp>Zatímco komplexní datové typy (objekty, pole atd.) jsou uložené v heapu, v call stacku se ukládají pouze reference\u002Fpointery do heapu. Co je naopak v call stacku reálně uložené, jsou hodnoty primitivních datových typů (string, integer, boolean, atd.), ale celý \u003Cstrong>exekuční kontext\u003C\u002Fstrong> funkce, čímž se myslí vše,, „co je potřeba k volání funkce“. Opět by bylo vhodné se hlouběji ponořit do toho, co všechno exekuční kontext funkce znamená, ale pro zjednodušení exekuční kontext zahrnuje:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Variable Environment (Prostředí proměnných)\u003C\u002Fstrong>: Záznam o proměnných a deklaracích funkcí specifických pro danou funkci.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Lexical Environment (Lexikální prostředí)\u003C\u002Fstrong>: Odkazy na nadřazený scope (lexikální prostředí) a proměnné definované pomocí let a const.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Vazba this\u003C\u002Fstrong>: Hodnota this v aktuální funkci.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Specifické detaily volání\u003C\u002Fstrong>: Argumenty předané funkci a mechanismus jejího návratu (tzn. místo v programu, kam se má program vrátit po dokončení funkce).\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>Když nechám Claude vygenerovat náhodný příklad pro ukázání, jak funguje call stack, mějme třeba následující kód:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-js\">function multiply(x, y) {\n  const result = x * y;\n  return result;\n}\n\nfunction calculate() {\n  const a = 10;\n  const b = 20;\n  const product = multiply(a, b);\n  console.log(product);\n}\n\ncalculate();\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Co se v takto jednoduchém příkladu přesně děje? Situace je následující: do \u003Cstrong>call stacku\u003C\u002Fstrong> se nejdříve uloží funkce \u003Ccode>calculate()\u003C\u002Fcode>, respektive její celý exekuční kontext. Poté se do \u003Cstrong>call stacku\u003C\u002Fstrong> přidá exekuční context funkce \u003Ccode>multiply()\u003C\u002Fcode>. Jakmile funkce \u003Ccode>multiply()\u003C\u002Fcode> je zavolána a její výsledek přiřazen do proměnné \u003Ccode>product\u003C\u002Fcode>, funkce \u003Ccode>multiply()\u003C\u002Fcode> a jeji exekuční kontext jsou odebrány z call stacku. Pak se do call stacku uloží exekuční kontext \u003Ccode>console.log()\u003C\u002Fcode>. Po vypsání hodnoty „200“ do konzole prohlížeče se exekuční kontext \u003Ccode>console.log()\u003C\u002Fcode> z call stacku odebere. Nakonec se odebere i exekuční kontext funkce \u003Ccode>calculate()\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>Všimněme si, že v příkladu nejsou globální proměnné a že volání funkce calculate() samo o sobě není zdánlivě v žádné funkce, jejíž exekuční kontext by mohlo být přidán do call stacku. Jak se tedy globální proměnné a volání funkcí v globálním scope dostane do call stacku?\u003C\u002Fp>\n\u003Ch4>Global Execution Context (EGC)\u003C\u002Fh4>\n\u003Cp>Vše, co je v globálním scope, lze chápat tak, že se volá uvnitř jedné hlavní funkce, jejíž exekuční kontext má speciální název i chování: \u003Cstrong>global execution context\u003C\u002Fstrong>. Upravme výše použitý příklad a přidáme do něj globální proměnné.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-js\">var globalA = 'testGlobalA';\nconst globalB = 'testGlobalB';\n\nfunction multiply(x, y) {\n  const result = x * y;\n  return result;\n}\n\nfunction calculate() {\n  const a = 10;\n  const b = 20;\n  const product = multiply(a, b);\n  console.log(product);\n}\n\ncalculate();\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Budeme-li brát, že globální kontext našich skriptů se bere jako jakási „main“ funkce, náš skript se bude chovat následovně: do call stacku se přidá globální exekuční kontext, do \u003Cstrong>prostředí proměnných\u003C\u002Fstrong> se přidá proměnná „globalA“, do \u003Cstrong>lexikální prostředí se přidá\u003C\u002Fstrong> proměnná „globalB“, vytvoří se \u003Cstrong>vazba this\u003C\u002Fstrong>, která bude v prostředí prohlížeče odkazovat na globální objekt \u003Cstrong>window\u003C\u002Fstrong>. Co se týče objektu \u003Cstrong>window\u003C\u002Fstrong>, je dobré připomenout, že zatímco \u003Cstrong>var global A\u003C\u002Fstrong> se přidá jako property do objektu \u003Cstrong>window\u003C\u002Fstrong>, proměnné definované pomocí \u003Cstrong>const\u003C\u002Fstrong> nebo \u003Cstrong>let\u003C\u002Fstrong> se přidají do globálního scope\u002Fglobálního lexikálního prostředí, ale nikoli do globálního objektu \u003Cstrong>window\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>Jakmile všechny funkce definované „uvnitř“ globálního exekučního kontextu jsou přidané do call stacku, zpracované, a odebraného z call stacku, zůstává v call stacku pouze globální exekuční kontext coby poslední funkce, a ta je nakonec také z call stacku odebrána, a call stack je prázdný.\u003C\u002Fp>\n\u003Cp>Takto to aspoň funguje v principu, pokud veškerý kód, který Javascript engine zpracovává, je \u003Cstrong>synchronní\u003C\u002Fstrong>. Pokud v naší aplikaci (čti uvnitř globálního exekučního kontextu) máme nějaké funkce, které jsou zpracovávány asynchronně, situace je značně odlišná.\u003C\u002Fp>\n\u003Cp>Řekli jsme, že \u003Cstrong>global execution context\u003C\u002Fstrong> (EGC) je jakousi prvotní „main“ funkcí a vstupním bodem aplikace. Zdá se, že musí běžet celou dobu, dokud není celý kód zpracován. Jak je to ale s asynchronními částmi kódu? Ty přece mohou spustit další funkce, které vracejí nové hodnoty a vytváří nové exekuční kontexty, takže globální exekuční kontext musí být v call stacku „nějak“ přítomen. A jak a kdy se volá asynchronní kód, když je call stack zaplněný částečně, nebo když je zcela prázdný? A pokud je call stack prázdný, jak je možné, aby se v Javascriptu něco spustilo, například, když uživatel klikne na tlačítko a spustí se callback event listeneru registrovaného pomocí addEventListener()? A co monitoruje asynchronní callbacky addEventListeneru, když Javascript engine je zcela synchronní, ba ani neobsahuje žádnou addEventListener funkci?\u003C\u002Fp>\n\u003Ch2>Asynchronie v Javascriptu aneb Web APIs, Task queues, Event loop\u003C\u002Fh2>\n\u003Cp>Engine programovacího jazyka Javascript neobsahuje ani objekt \u003Ccode>console\u003C\u002Fcode> ani její metodu \u003Ccode>console.log()\u003C\u002Fcode>. Což může být překvapení. Faktem je, že pro programátory běžné funkcionality typu \u003Ccode>setTimeout\u003C\u002Fcode>, \u003Ccode>addEventListener\u003C\u002Fcode>, operace pro manipulaci \u003Ccode>DOM\u003C\u002Fcode>, asynchronní síťové requesty \u003Ccode>fetch\u003C\u002Fcode> nebo \u003Ccode>XMLHttpRequest\u003C\u002Fcode> jsou součástí nikoli Javascript enginu, ale další komponenty Javascript runtime: Web API.\u003C\u002Fp>\n\u003Ch3>Web API\u003C\u002Fh3>\n\u003Cp>Jak přesně Javascript engine dokáže spolupracovat s Web API tak, aby Engine měl přístup k funkcím \u003Ccode>console.log()\u003C\u002Fcode> nebo \u003Ccode>setTimeout()\u003C\u002Fcode>, pokud nejsou přímo součástí engine? Javascript Engine (např. V8) má \u003Cstrong>interní registr nativních funkcí\u003C\u002Fstrong>, které jsou definovány v ECMAScript specifikaci (např. Math.random(), Array.prototype.map(), Date.now()). Podle toho Engine rozpozná, že \u003Ccode>setTimeout()\u003C\u002Fcode> není nativní funkcí, a proto si sáhne do globálního scope, respektive objektu \u003Ccode>window\u003C\u002Fcode>, a zkusí zavolat metodu \u003Ccode>window.setTimeout()\u003C\u002Fcode>. Javascript runtime totiž ještě před inicializací programu zajistí, že globální exekuční kontext (a tedy lexikální prostředí každé funkce, viz výše) obsahuje objekty, které jsou součástí Web API, např. document, navigator, performance, Intl, crypto, console.\u003C\u002Fp>\n\u003Cp>Když jsem se ptal ChatGPT, aby mi vysvětlilo proces spuštění metody \u003Ccode>console.log()\u003C\u002Fcode>, napsalo mi chybně, že \u003Ccode>console.log()\u003C\u002Fcode> je součástí samotného Javascript enginu. Když jsem upozornil na to, že tomu tak není, ChatGPT se omluvilo a nabídlo mi přesnější odpověď. Zajímalo mě, proč udělalo chybu. Svedlo to na to, že údajně mezi vývojáři (a tedy i blogery) je rozšířený mentální model fungování Javascriptu, kdy \u003Ccode>console.log()\u003C\u002Fcode> je synchronní operace, tudíž je zpracována přímo na call stacku (což je pravda), a z toho vyplývá, že \u003Cstrong>pokud se něco zpracovává přímo na call stacku (a nepotřebuje callbacky, které se zpracovávají asynchronně mimo hlavní vlákno Javascriptu, a tedy ani Event loop), musí to být součástí Javascript enginu.\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>To je chyba. Metoda \u003Ccode>console.log()\u003C\u002Fcode>neexistuje v Javascript enginu, ten o ní nic neví, kromě toho, že není nativní funkcí. Její konkrétní zpracování předává do Javascript runtime, kde prohlížeč (nebo nodejs) pak provede funkci pomocí nízkoúrovňového kódu v jazyce C++ pomocí C++ bindingů, což jsou vztahy mezi Javascript funkcí a C++ funkcí, která reálně vykonává to, co chceme po Javascript funkci.\u003C\u002Fp>\n\u003Cp>Zhruba již víme, jak to funguje, když Javascript engine potřebuje volat ne-nativní funkcí z Web API. Jak je to ale s tou asynchronií a voláním \u003Ccode>setTimeout()\u003C\u002Fcode>, \u003Ccode>addEventListener()\u003C\u002Fcode> či \u003Ccode>fetch()\u003C\u002Fcode> ?\u003C\u002Fp>\n\u003Ch3>Callbacks, Task Queues obecně\u003C\u002Fh3>\n\u003Cp>Jako single-threaded jazyk potřebuje Javascript engine delegovat vykonání asychronních operací mimo sebe. Javascript runtime obsahuje vedle Web API další komponenty, které asynchronní operace umožňují: Task queues a Event loop. Všechny asynchronní operace v Javascriptu jsou řešeny pomocí callbacků (a to i \u003Ccode>await\u002Fasync\u003C\u002Fcode>, které jsou sugar code využívající Promise-based asynchronicitu). Když Engine narazí na asynchronní operaci, předá její zpracování kompletně na Web API. Pokud se jedná o časovač jako \u003Ccode>setTimeout()\u003C\u002Fcode>, Web API čeká příslušný počet milisekund; pokud se jedná o network request jako \u003Ccode>fetch\u003C\u002Fcode>, opět je to na Web API, aby počkalo na příslušnou odpověď tohoto network requestu. Jakmile Web API dokončí zpracování samotné asynchronní operace, vezme programátorem definovaný \u003Cstrong>callback\u003C\u002Fstrong> a tuto callback funkci předá do speciální komponenty Javascript runtime, fronty nazvané \u003Cstrong>Task queue\u003C\u002Fstrong> anebo \u003Cstrong>Microtask\u003C\u002Fstrong> \u003Cstrong>queue\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>Callbacky zůstávají ve frontách, dokud není call stack zcela prázdný, respektive, pokud v něm nezůstává pouze globální exekuční kontext (GEK). Jakmile je v call stacku pouze GEK, příchází na řadu další komponenta: Event Loop.\u003C\u002Fp>\n\u003Ch3>Task queue\u003C\u002Fh3>\n\u003Cp>Task queue je starší z obou front a v jistém smyslu je stejně stará jako Javascript sám. Už první verze Javascriptu v Netscape Navigatoru uměly \u003Ccode>setTimeout()\u003C\u002Fcode> a reagovat na události vyvolané uživatelem, a obojí potřebovalo místo, kde callback počká, dokud engine nedokončí to, co právě dělá. Formálně ale Task queue (a Event loop vůbec) popsala až specifikace HTML5 organizace WHATWG, která sjednotila, jak se v tomto ohledu mají chovat všechny prohlížeče.\u003C\u002Fp>\n\u003Cp>Důvod vzniku Task queue je ten, se kterým jsme začali: single-threaded engine nesmí na nic čekat. Web API proto řeší čekání mimo engine a do Task queue pak předá už jen hotový callback, který se zpracuje, až na něj přijde řada. Do Task queue (specifikace HTML jim říká \u003Cstrong>tasks\u003C\u002Fstrong>, v blozích se často setkáme i s označením \u003Cstrong>macrotasks\u003C\u002Fstrong>) patří:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>callbacky časovačů \u003Ccode>setTimeout()\u003C\u002Fcode> a \u003Ccode>setInterval()\u003C\u002Fcode>,\u003C\u002Fli>\n\u003Cli>callbacky událostí registrovaných pomocí \u003Ccode>addEventListener()\u003C\u002Fcode> (kliknutí, stisk klávesy, scroll…),\u003C\u002Fli>\n\u003Cli>síťové události starších API, například \u003Ccode>onload\u003C\u002Fcode> u \u003Ccode>XMLHttpRequest\u003C\u002Fcode> nebo zprávy z \u003Ccode>WebSocket\u003C\u002Fcode>,\u003C\u002Fli>\n\u003Cli>zprávy mezi okny a workery posílané pomocí \u003Ccode>postMessage()\u003C\u002Fcode>,\u003C\u002Fli>\n\u003Cli>a vůbec samotné spuštění skriptu z tagu \u003Ccode>&lt;script&gt;\u003C\u002Fcode> – i náš globální exekuční kontext tedy začíná svůj život jako task.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Za zmínku stojí dvě věci. Zaprvé, „Task queue“ v jednotném čísle je zjednodušení. Specifikace dovoluje prohlížeči mít front několik (např. zvlášť pro uživatelské interakce a zvlášť pro časovače) a vybírat mezi nimi, takže prohlížeč může dát přednost kliknutí před časovačem. V rámci jedné fronty ale platí metoda FIFO. Zadruhé, \u003Cstrong>Event loop vezme z Task queue vždy jen jeden task\u003C\u002Fstrong> a mezi dvěma tasky má prohlížeč příležitost překreslit stránku. I \u003Ccode>setTimeout(callback, 0)\u003C\u002Fcode> proto neznamená „hned“, ale „nejdříve v příštím kole Event loopu“. Při hluboce vnořeném volání časovačů navíc prohlížeče zvedají minimální prodlevu na 4 ms.\u003C\u002Fp>\n\u003Ch3>Microtask queue\u003C\u002Fh3>\n\u003Cp>Microtask queue je o dost mladší. Pojem \u003Cstrong>microtask\u003C\u002Fstrong> se objevil kolem roku 2011, kdy inženýři z Mozilly a Chromia navrhovali náhradu za tzv. Mutation Events – starý mechanismus, kterým šlo sledovat změny v DOM. Mutation Events se spouštěly synchronně při každé jednotlivé změně DOM, což bylo pomalé a náchylné k chybám. Nové API \u003Ccode>MutationObserver\u003C\u002Fcode> mělo změny doručovat asynchronně a najednou v dávce, ale zároveň dříve, než prohlížeč stránku překreslí nebo zpracuje další událost. Task queue na to nestačila, protože mezi dvěma tasky už k překreslení nebo ke zpracování dalšího kliknutí dojít může. Proto vznikla nová fronta, jejíž callbacky se zpracují \u003Cstrong>hned, jakmile se call stack vyprázdní, ještě před dalším taskem\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>Druhým a dnes nejznámějším využitím Microtask queue se staly \u003Cstrong>Promises\u003C\u002Fstrong>, standardizované v ECMAScript 2015 (ES6). Specifikace ECMAScriptu jim říká „jobs“, prohlížeč je ale řadí právě do Microtask queue. Promises tím dostaly důležitou garanci: callback v \u003Ccode>.then()\u003C\u002Fcode> se nikdy nezavolá synchronně, ale ani zbytečně pozdě – vždy hned po dokončení aktuálně běžícího kódu. Kolem roku 2019 pak prohlížeče přidaly funkci \u003Ccode>queueMicrotask()\u003C\u002Fcode>, pomocí které může microtask naplánovat přímo i programátor, bez obcházení přes Promise.\u003C\u002Fp>\n\u003Cp>Do Microtask queue patří:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>callbacky Promise: \u003Ccode>.then()\u003C\u002Fcode>, \u003Ccode>.catch()\u003C\u002Fcode>, \u003Ccode>.finally()\u003C\u002Fcode>,\u003C\u002Fli>\n\u003Cli>pokračování async funkce za \u003Ccode>await\u003C\u002Fcode> (async\u002Fawait je postavené nad Promises),\u003C\u002Fli>\n\u003Cli>callbacky předané do \u003Ccode>queueMicrotask()\u003C\u002Fcode>,\u003C\u002Fli>\n\u003Cli>callbacky \u003Ccode>MutationObserver\u003C\u002Fcode>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>A ještě jedno důležité pravidlo: zatímco z Task queue bere Event loop vždy v daný okamžik právě jeden task, \u003Cstrong>Microtask queue vyprázdní vždy celou\u003C\u002Fstrong>, včetně microtasků, které přibyly během jejího zpracování. Pokud by nějaký microtask přidal další microtask, prohlížeč by se ke kliknutí ani k překreslení stránky už nedostal a stránka by zamrzla – stejně jako při nekonečném cyklu v synchronním kódu.\u003C\u002Fp>\n\u003Ch2>Event Loop\u003C\u002Fh2>\n\u003Cp>Mysteriózní Event loop se nyní ukáže jako něco relativně triviálního. Event loop není nic jiného než jedna komponenta v Javascript runtime, která neustále kontroluje, zda call stack je plný. Pokud je prázdný, je dalším úkolem Event loopu rozhodnout, zda vzít metodou FIFO první callback z Task queue nebo Microtask queue. Toť vše.\u003C\u002Fp>\n\u003Csvg id=\"el-anim\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" viewBox=\"0 0 480 764\" role=\"img\" aria-label=\"Animace: jak Event Loop zpracuje úvodní příklad\" aria-describedby=\"el-anim-desc\" style=\"display:block;width:100%;max-width:560px;height:auto;margin:2.5rem auto\">\n\u003Cdesc id=\"el-anim-desc\">Skript běží v globálním exekučním kontextu. setTimeout předá časovač Web API a jeho callback se po 0 ms zařadí do Task queue. Callback z .then() splněné Promise se zařadí do Microtask queue. console.log proběhne synchronně a do konzole vypíše „hello from console log“. Když se call stack vyprázdní, Event Loop nejdřív zpracuje Microtask queue a vypíše „ahoy from promise“, teprve potom vezme task z Task queue a vypíše „hey, from set timeout“.\u003C\u002Fdesc>\n\u003Cstyle>\n#el-anim{font-family:\"EB Garamond\",Georgia,serif}\n#el-anim .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;fill:#1a1a1a}\n#el-anim .code{font-size:12px}\n#el-anim .ui{font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif;font-size:11px;fill:#5f6368}\n#el-anim .panel{fill:#fafafa;stroke:#d0d0d0;stroke-width:1.5}\n#el-anim .label{font-size:16px;font-weight:600;fill:#1a1a1a}\n#el-anim .hint{font-size:12px;fill:#8a8a8a}\n#el-anim .cap text{font-size:15px;fill:#1a1a1a}\n#el-anim .frame rect{fill:#eeeeee;stroke:#8a8a8a;stroke-width:1.2}\n#el-anim .cb rect{fill:#e6f0e6;stroke:#006400;stroke-width:1.2}\n#el-anim .timer rect{fill:#fff6db;stroke:#b8860b;stroke-width:1.2}\n#el-anim .hl{fill:#fff1a8}\n#el-anim .rule{stroke:#e8eaed;stroke-width:1}\n#el-anim .anim{animation-duration:30s;animation-iteration-count:infinite;animation-timing-function:ease-in-out;animation-fill-mode:both}\n#el-anim .btn{cursor:pointer}\n#el-anim .btn:focus{outline:none}\n#el-anim .btn rect{fill:#ffffff;stroke:#c8c8c8;stroke-width:1.2}\n#el-anim .btn:hover rect,#el-anim .btn:focus-visible rect{fill:#eef6ee;stroke:#006400}\n#el-anim .btn text{font-size:14px;fill:#1a1a1a}\n#el-anim .btn .ico{fill:#1a1a1a}\n#el-anim .tok,#el-anim .hl,#el-anim .cap{opacity:0}\n#el-anim .out,#el-anim .cap-end{opacity:1}\n#el-anim .loop{color:#9a9a9a}\n#el-anim .spin{transform-box:fill-box;transform-origin:center;animation:elAnim-spin 3s linear infinite}\n#el-anim .caret{animation:elAnim-blink 1.1s steps(1) infinite}\n#el-anim .g-global{animation-name:elAnim-global}\n#el-anim .g-set{animation-name:elAnim-set}\n#el-anim .g-timer{animation-name:elAnim-timer}\n#el-anim .g-cb1{animation-name:elAnim-cb1}\n#el-anim .g-prom{animation-name:elAnim-prom}\n#el-anim .g-cb2{animation-name:elAnim-cb2}\n#el-anim .g-log{animation-name:elAnim-log}\n#el-anim .g-in2{animation-name:elAnim-in2}\n#el-anim .g-in3{animation-name:elAnim-in3}\n#el-anim .hl1{animation-name:elAnim-hl1}\n#el-anim .hl2{animation-name:elAnim-hl2}\n#el-anim .hl3{animation-name:elAnim-hl3}\n#el-anim .hl4{animation-name:elAnim-in3}\n#el-anim .hl5{animation-name:elAnim-in2}\n#el-anim .out1{animation-name:elAnim-out1}\n#el-anim .out2{animation-name:elAnim-out2}\n#el-anim .out3{animation-name:elAnim-out3}\n#el-anim .loop{animation-name:elAnim-loop}\n#el-anim .mlane{animation-name:elAnim-mlane}\n#el-anim .tlane{animation-name:elAnim-tlane}\n#el-anim .c1{animation-name:elAnim-c1}\n#el-anim .c2{animation-name:elAnim-c2}\n#el-anim .c3{animation-name:elAnim-c3}\n#el-anim .c4{animation-name:elAnim-c4}\n#el-anim .c5{animation-name:elAnim-c5}\n#el-anim .c6{animation-name:elAnim-c6}\n#el-anim .c7{animation-name:elAnim-c7}\n#el-anim .c8{animation-name:elAnim-c8}\n@keyframes elAnim-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}\n@keyframes elAnim-blink{0%{opacity:1}50%{opacity:0}}\n@keyframes elAnim-global{0%{opacity:0}2%,50%{opacity:1}52%,100%{opacity:0}}\n@keyframes elAnim-set{0%,8%{opacity:0}9%,16%{opacity:1}18%,100%{opacity:0}}\n@keyframes elAnim-timer{0%,12%{opacity:0}13%,18%{opacity:1}20%,100%{opacity:0}}\n@keyframes elAnim-cb1{0%,19%{opacity:0;transform:translate(258px,468px)}20%{opacity:1;transform:translate(258px,468px)}23%,78%{opacity:1;transform:translate(26px,468px)}82%,90%{opacity:1;transform:translate(26px,320px)}92%,100%{opacity:0;transform:translate(26px,320px)}}\n@keyframes elAnim-prom{0%,24%{opacity:0}25%,33%{opacity:1}35%,100%{opacity:0}}\n@keyframes elAnim-cb2{0%,30%{opacity:0;transform:translate(258px,398px)}31%{opacity:1;transform:translate(258px,398px)}34%,60%{opacity:1;transform:translate(26px,398px)}64%,72%{opacity:1;transform:translate(26px,320px)}74%,100%{opacity:0;transform:translate(26px,320px)}}\n@keyframes elAnim-log{0%,38%{opacity:0}39%,46%{opacity:1}48%,100%{opacity:0}}\n@keyframes elAnim-in2{0%,65%{opacity:0}66%,70%{opacity:1}72%,100%{opacity:0}}\n@keyframes elAnim-in3{0%,83%{opacity:0}84%,88%{opacity:1}90%,100%{opacity:0}}\n@keyframes elAnim-hl1{0%,8%{opacity:0}9%,16%{opacity:1}18%,100%{opacity:0}}\n@keyframes elAnim-hl2{0%,24%{opacity:0}25%,33%{opacity:1}35%,100%{opacity:0}}\n@keyframes elAnim-hl3{0%,38%{opacity:0}39%,46%{opacity:1}48%,100%{opacity:0}}\n@keyframes elAnim-out1{0%,42%{opacity:0}43%,97%{opacity:1}100%{opacity:0}}\n@keyframes elAnim-out2{0%,67%{opacity:0}68%,97%{opacity:1}100%{opacity:0}}\n@keyframes elAnim-out3{0%,85%{opacity:0}86%,97%{opacity:1}100%{opacity:0}}\n@keyframes elAnim-loop{0%,53%{color:#9a9a9a}55%,91%{color:#006400}93%,100%{color:#9a9a9a}}\n@keyframes elAnim-mlane{0%,55%{fill:#fafafa;stroke:#d0d0d0}56%,64%{fill:#eef6ee;stroke:#006400}66%,100%{fill:#fafafa;stroke:#d0d0d0}}\n@keyframes elAnim-tlane{0%,73%{fill:#fafafa;stroke:#d0d0d0}74%,82%{fill:#eef6ee;stroke:#006400}84%,100%{fill:#fafafa;stroke:#d0d0d0}}\n@keyframes elAnim-c1{0%,7%{opacity:1}8%,100%{opacity:0}}\n@keyframes elAnim-c2{0%,7%{opacity:0}8%,23%{opacity:1}24%,100%{opacity:0}}\n@keyframes elAnim-c3{0%,23%{opacity:0}24%,37%{opacity:1}38%,100%{opacity:0}}\n@keyframes elAnim-c4{0%,37%{opacity:0}38%,49%{opacity:1}50%,100%{opacity:0}}\n@keyframes elAnim-c5{0%,49%{opacity:0}50%,55%{opacity:1}56%,100%{opacity:0}}\n@keyframes elAnim-c6{0%,55%{opacity:0}56%,73%{opacity:1}74%,100%{opacity:0}}\n@keyframes elAnim-c7{0%,73%{opacity:0}74%,91%{opacity:1}92%,100%{opacity:0}}\n@keyframes elAnim-c8{0%,91%{opacity:0}92%,99%{opacity:1}100%{opacity:0}}\n@media (prefers-reduced-motion:reduce){#el-anim .anim,#el-anim .spin,#el-anim .caret{animation:none}#el-anim .controls{display:none}}\n\u003C\u002Fstyle>\n\u003Crect class=\"panel\" x=\"16\" y=\"16\" width=\"448\" height=\"140\" rx=\"10\"\u002F>\n\u003Crect class=\"hl hl1 anim\" x=\"20\" y=\"26\" width=\"440\" height=\"60\" rx=\"4\"\u002F>\n\u003Crect class=\"hl hl2 anim\" x=\"20\" y=\"86\" width=\"440\" height=\"40\" rx=\"4\"\u002F>\n\u003Crect class=\"hl hl3 anim\" x=\"20\" y=\"126\" width=\"440\" height=\"20\" rx=\"4\"\u002F>\n\u003Crect class=\"hl hl4 anim\" x=\"20\" y=\"46\" width=\"440\" height=\"20\" rx=\"4\"\u002F>\n\u003Crect class=\"hl hl5 anim\" x=\"20\" y=\"106\" width=\"440\" height=\"20\" rx=\"4\"\u002F>\n\u003Ctext class=\"mono code\" x=\"28\" y=\"40\">setTimeout(() =&gt; {\u003C\u002Ftext>\n\u003Ctext class=\"mono code\" x=\"42.4\" y=\"60\">console.log('hey, from set timeout');\u003C\u002Ftext>\n\u003Ctext class=\"mono code\" x=\"28\" y=\"80\">}, 0);\u003C\u002Ftext>\n\u003Ctext class=\"mono code\" x=\"28\" y=\"100\">new Promise((resolve) =&gt; resolve('ahoy from promise'))\u003C\u002Ftext>\n\u003Ctext class=\"mono code\" x=\"42.4\" y=\"120\">.then((v) =&gt; console.log(v));\u003C\u002Ftext>\n\u003Ctext class=\"mono code\" x=\"28\" y=\"140\">console.log('hello from console log');\u003C\u002Ftext>\n\u003Crect class=\"panel\" x=\"16\" y=\"170\" width=\"216\" height=\"190\" rx=\"10\"\u002F>\n\u003Ctext class=\"label\" x=\"28\" y=\"192\">Call stack\u003C\u002Ftext>\n\u003Crect class=\"panel\" x=\"248\" y=\"170\" width=\"216\" height=\"90\" rx=\"10\"\u002F>\n\u003Ctext class=\"label\" x=\"260\" y=\"192\">Web APIs\u003C\u002Ftext>\n\u003Crect class=\"panel\" x=\"248\" y=\"272\" width=\"216\" height=\"88\" rx=\"10\"\u002F>\n\u003Cg class=\"loop anim\">\n\u003Cg class=\"spin\">\n\u003Ccircle cx=\"276\" cy=\"318\" r=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-dasharray=\"90 23\"\u002F>\n\u003Cpolygon points=\"286.9,302.4 282.4,296.4 279.8,305.0\" fill=\"currentColor\"\u002F>\n\u003C\u002Fg>\n\u003Ctext x=\"306\" y=\"306\" style=\"font-size:18px;font-weight:600\" fill=\"currentColor\">Event Loop\u003C\u002Ftext>\n\u003C\u002Fg>\n\u003Ctext class=\"hint\" x=\"306\" y=\"325\">prázdný stack? → nejdřív\u003C\u002Ftext>\n\u003Ctext class=\"hint\" x=\"306\" y=\"340\">všechny microtasky, pak task\u003C\u002Ftext>\n\u003Crect class=\"panel mlane anim\" x=\"16\" y=\"376\" width=\"448\" height=\"58\" rx=\"10\"\u002F>\n\u003Ctext class=\"label\" x=\"28\" y=\"392\" style=\"font-size:15px\">Microtask queue\u003C\u002Ftext>\n\u003Ctext class=\"hint\" x=\"454\" y=\"392\" text-anchor=\"end\">Promise, queueMicrotask, MutationObserver\u003C\u002Ftext>\n\u003Crect class=\"panel tlane anim\" x=\"16\" y=\"446\" width=\"448\" height=\"58\" rx=\"10\"\u002F>\n\u003Ctext class=\"label\" x=\"28\" y=\"462\" style=\"font-size:15px\">Task queue\u003C\u002Ftext>\n\u003Ctext class=\"hint\" x=\"454\" y=\"462\" text-anchor=\"end\">setTimeout, události, síťové operace\u003C\u002Ftext>\n\u003Crect x=\"16\" y=\"520\" width=\"448\" height=\"144\" rx=\"8\" fill=\"#ffffff\" stroke=\"#cfd1d4\" stroke-width=\"1.5\"\u002F>\n\u003Cpath d=\"M16 546 V528 a8 8 0 0 1 8 -8 H456 a8 8 0 0 1 8 8 V546 Z\" fill=\"#f1f3f4\"\u002F>\n\u003Ctext class=\"ui\" x=\"28\" y=\"537\">Elements\u003C\u002Ftext>\n\u003Ctext class=\"ui\" x=\"90\" y=\"537\" style=\"fill:#202124\">Console\u003C\u002Ftext>\n\u003Ctext class=\"ui\" x=\"146\" y=\"537\">Sources\u003C\u002Ftext>\n\u003Ctext class=\"ui\" x=\"199\" y=\"537\">Network\u003C\u002Ftext>\n\u003Crect x=\"84\" y=\"543\" width=\"52\" height=\"3\" fill=\"#1a73e8\"\u002F>\n\u003Cline class=\"rule\" x1=\"16\" y1=\"546\" x2=\"464\" y2=\"546\"\u002F>\n\u003Ccircle cx=\"31\" cy=\"558\" r=\"5.5\" fill=\"none\" stroke=\"#5f6368\" stroke-width=\"1.2\"\u002F>\n\u003Cline x1=\"27.1\" y1=\"561.9\" x2=\"34.9\" y2=\"554.1\" stroke=\"#5f6368\" stroke-width=\"1.2\"\u002F>\n\u003Ctext class=\"ui\" x=\"46\" y=\"562\">top ▾\u003C\u002Ftext>\n\u003Crect x=\"84\" y=\"551\" width=\"150\" height=\"15\" rx=\"3\" fill=\"#ffffff\" stroke=\"#dadce0\"\u002F>\n\u003Ctext class=\"ui\" x=\"90\" y=\"562\" style=\"fill:#9aa0a6\">Filter\u003C\u002Ftext>\n\u003Ctext class=\"ui\" x=\"454\" y=\"562\" text-anchor=\"end\">Default levels ▾\u003C\u002Ftext>\n\u003Cline class=\"rule\" x1=\"16\" y1=\"570\" x2=\"464\" y2=\"570\"\u002F>\n\u003Cg class=\"out out1 anim\">\u003Ctext class=\"mono\" x=\"28\" y=\"584\">hello from console log\u003C\u002Ftext>\u003Ctext class=\"ui\" x=\"454\" y=\"584\" text-anchor=\"end\" style=\"text-decoration:underline\">script.js:6\u003C\u002Ftext>\u003Cline class=\"rule\" x1=\"16\" y1=\"590\" x2=\"464\" y2=\"590\"\u002F>\u003C\u002Fg>\n\u003Cg class=\"out out2 anim\">\u003Ctext class=\"mono\" x=\"28\" y=\"604\">ahoy from promise\u003C\u002Ftext>\u003Ctext class=\"ui\" x=\"454\" y=\"604\" text-anchor=\"end\" style=\"text-decoration:underline\">script.js:5\u003C\u002Ftext>\u003Cline class=\"rule\" x1=\"16\" y1=\"610\" x2=\"464\" y2=\"610\"\u002F>\u003C\u002Fg>\n\u003Cg class=\"out out3 anim\">\u003Ctext class=\"mono\" x=\"28\" y=\"624\">hey, from set timeout\u003C\u002Ftext>\u003Ctext class=\"ui\" x=\"454\" y=\"624\" text-anchor=\"end\" style=\"text-decoration:underline\">script.js:2\u003C\u002Ftext>\u003Cline class=\"rule\" x1=\"16\" y1=\"630\" x2=\"464\" y2=\"630\"\u002F>\u003C\u002Fg>\n\u003Ctext x=\"26\" y=\"646\" style=\"font-size:14px;font-weight:700\" fill=\"#1a73e8\">›\u003C\u002Ftext>\n\u003Crect class=\"caret\" x=\"38\" y=\"636\" width=\"1.2\" height=\"12\" fill=\"#202124\"\u002F>\n\u003Cg class=\"tok frame g-global anim\" transform=\"translate(26 320)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">global execution context\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok frame g-set anim\" transform=\"translate(26 284)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">setTimeout(…, 0)\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok timer g-timer anim\" transform=\"translate(258 208)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">časovač: 0 ms\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok frame g-prom anim\" transform=\"translate(26 284)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">new Promise(…).then(…)\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok frame g-log anim\" transform=\"translate(26 284)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">console.log('hello…')\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok frame g-in2 anim\" transform=\"translate(26 284)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">console.log(v)\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok frame g-in3 anim\" transform=\"translate(26 284)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">console.log('hey…')\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok cb g-cb2 anim\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">(v) =&gt; console.log(v)\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok cb g-cb1 anim\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">() =&gt; { console.log(…) }\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c1 anim\">\u003Ctext x=\"16\" y=\"690\">1. Skript se spouští jako task. Do call stacku přijde\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">globální exekuční kontext.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c2 anim\">\u003Ctext x=\"16\" y=\"690\">2. setTimeout() předá časovač Web API. Po 0 ms\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">se jeho callback zařadí do Task queue.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c3 anim\">\u003Ctext x=\"16\" y=\"690\">3. Promise je hned splněná, takže callback z&#160;.then()\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">míří do Microtask queue.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c4 anim\">\u003Ctext x=\"16\" y=\"690\">4. console.log() proběhne synchronně přímo na call stacku\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">a&#160;vypíše do konzole první řádek.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c5 anim\">\u003Ctext x=\"16\" y=\"690\">5. Skript doběhl a&#160;call stack je prázdný.\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">Nastupuje Event Loop.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c6 anim\">\u003Ctext x=\"16\" y=\"690\">6. Event Loop nejdřív vyprázdní Microtask queue:\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">callback z&#160;.then() vypíše „ahoy from promise“.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c7 anim\">\u003Ctext x=\"16\" y=\"690\">7. Microtask queue je prázdná, Event Loop tedy vezme\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">první task z Task queue: callback ze setTimeout().\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap cap-end c8 anim\">\u003Ctext x=\"16\" y=\"690\">Výsledek: „hello“, „ahoy“, „hey“ – nejdřív synchronní\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">kód, pak všechny microtasky a&#160;teprve potom tasky.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"controls\">\n\u003Cg class=\"btn\" role=\"button\" tabindex=\"0\" aria-label=\"Přehrát animaci od začátku\" onclick=\"this.ownerSVGElement.getAnimations({subtree:true}).forEach(function(a){a.currentTime=0;a.play()})\" onkeydown=\"if(event.key==='Enter'||event.key===' '){event.preventDefault();this.onclick()}\">\u003Crect x=\"16\" y=\"731\" width=\"112\" height=\"24\" rx=\"12\"\u002F>\u003Crect class=\"ico\" x=\"27\" y=\"737\" width=\"2\" height=\"12\"\u002F>\u003Cpolygon class=\"ico\" points=\"37,737 37,749 30,743\"\u002F>\u003Ctext x=\"44\" y=\"747\">Od začátku\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"btn\" role=\"button\" tabindex=\"0\" aria-label=\"Zastavit animaci\" onclick=\"this.ownerSVGElement.getAnimations({subtree:true}).forEach(function(a){a.pause()})\" onkeydown=\"if(event.key==='Enter'||event.key===' '){event.preventDefault();this.onclick()}\">\u003Crect x=\"136\" y=\"731\" width=\"96\" height=\"24\" rx=\"12\"\u002F>\u003Crect class=\"ico\" x=\"147\" y=\"737\" width=\"3\" height=\"12\"\u002F>\u003Crect class=\"ico\" x=\"153\" y=\"737\" width=\"3\" height=\"12\"\u002F>\u003Ctext x=\"164\" y=\"747\">Zastavit\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"btn\" role=\"button\" tabindex=\"0\" aria-label=\"Pokračovat v animaci\" onclick=\"this.ownerSVGElement.getAnimations({subtree:true}).forEach(function(a){a.play()})\" onkeydown=\"if(event.key==='Enter'||event.key===' '){event.preventDefault();this.onclick()}\">\u003Crect x=\"240\" y=\"731\" width=\"112\" height=\"24\" rx=\"12\"\u002F>\u003Cpolygon class=\"ico\" points=\"251,737 251,749 260,743\"\u002F>\u003Ctext x=\"266\" y=\"747\">Pokračovat\u003C\u002Ftext>\u003C\u002Fg>\n\u003C\u002Fg>\n\u003C\u002Fsvg>\n",false,{"rendered":19,"protected":17},"",0,"closed","standard",{},[25],5,[],[28,32,36],{"id":29,"name":30,"slug":30,"taxonomy":31},10667210,"javascript","post_tag",{"id":33,"name":34,"slug":35,"taxonomy":31},10669219,"event loop","event-loop",{"id":37,"name":38,"slug":39,"taxonomy":31},10017291,"software engineering","software-engineering",{"en":41,"es":45,"fr":49},{"title":42,"slug":43,"description":44},"When We Say Event Loop","when-we-say-event-loop","How the JavaScript event loop decides the order of setTimeout, Promise and console.log: the engine, the call stack, Web APIs, the Task queue and the Microtask queue, step by step.",{"title":46,"slug":47,"description":48},"Cuando decimos Event Loop","cuando-decimos-event-loop","Cómo decide el event loop de JavaScript el orden de setTimeout, Promise y console.log: el engine, el call stack, las Web APIs, la Task queue y la Microtask queue, paso a paso.",{"title":50,"slug":51,"description":52},"Quand on dit Event Loop","quand-on-dit-event-loop","Comment l’event loop de JavaScript décide de l’ordre de setTimeout, Promise et console.log : l’engine, le call stack, les Web APIs, la Task queue et la Microtask queue, pas à pas.","\u003Cp>The work of a software developer requires that we keep learning new things and follow the trends in the field. Whether to improve our position on the job market or simply to deepen our knowledge and productivity. Over the last two years, the things we have to follow came to include the first signs of artificial intelligence, in the form of various AI assistants, co-pilots and ChatGPT integrations that can replace common, routine processes and also generate solutions to more complex problems. Today, at the time of writing, the leading language models can generate a whole project from a single prompt in Claude Code Fable or ChatGPT Astra, a project that can be deployed, tested and run. From software and medicine to copywriting and translation, professionals in these fields now face waves of layoffs. This so-called \u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTechnological_unemployment\">technological unemployment\u003C\u002Fa> is no longer discussed only in academia. It has become a topic and an everyday reality of white-collar professions, see \u003Ca href=\"https:\u002F\u002Fwww.technologyreview.com\u002F2024\u002F01\u002F27\u002F1087041\u002Ftechnological-unemployment-elon-musk-jobs-ai\u002F\">here\u003C\u002Fa> or \u003Ca href=\"https:\u002F\u002Fwww.ilo.org\u002Fresource\u002Farticle\u002Fminimizing-negative-effects-ai-induced-technological-unemployment\">here\u003C\u002Fa>. It is not the first time, nor the last, that advances in technology make many jobs and fields irrelevant and, in turn, create new ones. In such cases the \u003Ca href=\"https:\u002F\u002Fdictionary.cambridge.org\u002Fdictionary\u002Fenglish\u002Fseparate-the-wheat-from-the-chaff\">wheat is separated from the chaff\u003C\u002Fa>, and it is legitimate to ask who will remain useful to corporations in this AI-powered market economy?\u003C\u002Fp>\n\u003Cp>Junior positions seem to be the first and natural target. Superficial knowledge, an undeveloped ability to see technical problems in the wider context of technical, but also business requirements. AI is ceasing to suffer from this, whereas junior positions are known for it.\u003C\u002Fp>\n\u003Cp>When I look at my own field of front-end and the back-end server runtimes node\u002Fdeno\u002Fbun, a deeper knowledge of the Event Loop seems to me to be one of the few so-called “hard skills” that, in the world of Javascript, separate a junior coder from a senior software engineer. By understanding the Event Loop one can answer several important questions, for example: how is it possible that Javascript is a single-threaded programming language, and yet we do several things in it at once by means of asynchronous operations? Much could be written about the Event Loop. My ambitions are modest. I will look at the Event Loop in a way that lets me explain one of the most common questions at a job interview for a front-end developer position: in what order will the following operations be executed, and why:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-js\">setTimeout(() =&gt; {\n  console.log('hey, from set timeout');\n}, 0);\n\nnew Promise((resolve) =&gt; resolve('ahoy from promise')).then((v) =&gt;\n  console.log(v)\n);\n\nconsole.log('hello from console log');\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>A technical introduction to the problem\u003C\u002Fh2>\n\u003Cp>Javascript is \u003Cstrong>single-threaded\u003C\u002Fstrong>. If we leave aside the \u003Ca href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FWeb_Workers_API\">Web Workers API\u003C\u002Fa>, this means that Javascript can process only a single operation at any one moment. Javascript is designed to process operations sequentially by the LIFO method (Last-In, First-Out), and until an operation is finished, it blocks all the operations that follow. Under normal circumstances, for example, after a click on a button one slow GET request to a remote API would block all user interaction; the whole user interface of the browser would freeze until the network request was settled. When Brendan Eich designed Javascript in the nineties, the processing of operations in Javascript was synchronous, with limited means of handling the events raised by the user’s interaction with the browser. It would be worth a historical probe to find out why Eich designed Javascript this way. Today, however, we know that web pages built on Javascript (that is, the whole web) do not behave like this. A user can interact with several parts of a web page at once. How can a single-threaded programming language achieve that?\u003C\u002Fp>\n\u003Ch2>Enter Javascript runtime\u003C\u002Fh2>\n\u003Cp>When we say Javascript, we have to realise that we are talking about a \u003Cstrong>collection of libraries and components\u003C\u002Fstrong>. Only by connecting them do we get a working whole – the \u003Cstrong>runtime\u003C\u002Fstrong>, the environment in which our application runs. At present we have a choice of two main types of runtime. It is either the runtime of a web browser, or the runtime of one of several server implementations, for example Node.js, Deno, or the newest one, Bun. Although runtimes in browsers and on servers share common elements, we also find important differences between them. These follow from the focus of the given type of runtime. Web browsers are oriented towards working with the DOM (\u003Ccode>window\u003C\u002Fcode>, \u003Ccode>document\u003C\u002Fcode>), user interaction (\u003Ccode>addEventListener\u003C\u002Fcode>), network operations by means of \u003Ccode>fetch\u003C\u002Fcode> or \u003Ccode>XMLHttpRequest\u003C\u002Fcode>, local storage in the browser (\u003Ccode>localStorage\u003C\u002Fcode>, \u003Ccode>sessionStorage\u003C\u002Fcode>) and so on. The Node.js or Bun runtime, by contrast, is oriented towards the environment of server applications, work with the file system and databases, or low-level operations on the operating system. \u003Cstrong>Both types of runtime, however, implement the Event Loop\u003C\u002Fstrong>. To keep things simple, from here on I will concern myself only with the runtime of web browsers.\u003C\u002Fp>\n\u003Ch2>The runtime of web browsers\u003C\u002Fh2>\n\u003Cp>The Javascript runtime of web browsers consists of the following parts:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Javascript Engine\u003C\u002Fli>\n\u003Cli>Web APIs\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Event Loop\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>Task queue and Microtask queue\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>The Event Loop is only one of the components, and it comes into play only when asynchronous code is processed. If our Javascript program uses only synchronous operations, the Event Loop will never be used. Even so, the Event Loop is one of the defining characteristics of Javascript, and thanks to it today’s modern web applications are full of asynchronous functionality. To understand the Event Loop, and the processing of asynchronous code in Javascript at all, it is good to recall \u003Cstrong>how Javascript behaves with fully synchronous operations\u003C\u002Fstrong>. For that, in turn, it is good to understand how the Javascript engine works.\u003C\u002Fp>\n\u003Ch3>Javascript engine\u003C\u002Fh3>\n\u003Cp>The imaginary heart of Javascript is the engine. The main functions of a JavaScript engine are parsing, interpreting and executing code. By parsing we mean \u003Cstrong>the conversion of JavaScript source code into a so-called abstract syntax tree (AST)\u003C\u002Fstrong>, a structure the engine is able to process. By interpretation we mean that \u003Cstrong>most modern engines first interpret the code and then compile it into machine code (just-in-time compilation – JIT)\u003C\u002Fstrong>. By execution we then mean that \u003Cstrong>after the translation into machine code the engine starts carrying out the instructions and keeps the application running\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>Examples of popular JavaScript engines:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>V8: Used by Google Chrome and Node.js. It is known for its speed and efficiency.\u003C\u002Fli>\n\u003Cli>SpiderMonkey: The first JavaScript engine, developed by Netscape, used today in Firefox.\u003C\u002Fli>\n\u003Cli>JavaScriptCore (aka Nitro): Used by Safari and other Apple products.\u003C\u002Fli>\n\u003Cli>Chakra: Used by Microsoft Edge (legacy), now replaced by the V8 engine in the new Edge (Chromium).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>The JavaScript engine further provides two memory structures: the \u003Cstrong>heap\u003C\u002Fstrong> and the \u003Cstrong>call stack\u003C\u002Fstrong>. Their differences and workings would again deserve a separate article on memory management in Javascript. For a basic definition, read Wikipedia or prompt it out of ChatGPT.\u003C\u002Fp>\n\u003Cp>What matters for understanding the Event Loop is that \u003Cstrong>the Javascript Engine as such does not implement the processing of asynchronous code\u003C\u002Fstrong>. All of Javascript’s asynchronicity has to be implemented elsewhere. Where? That is exactly the job of the Javascript Runtime, which adds to the Javascript engine the Web APIs (we are in the browser), the Event loop and the waiting queues, the Task queue and the Microtask queue. Fun fact: if Javascript were a purely synchronous programming language, it would get by with the heap and the call stack alone and would not need an Event Loop. But I am getting ahead of myself.\u003C\u002Fp>\n\u003Ch3>Heap\u003C\u002Fh3>\n\u003Cp>The \u003Cstrong>heap\u003C\u002Fstrong> is not all that important for understanding the Event Loop. The heap is a memory structure in which the Javascript Engine stores complex data types (Object, Array, Function, Map, Set, WeakMap, WeakSet). The size of the heap is given by the size of the RAM. Unlike the call stack, the heap is not organised sequentially; it is non-linear memory. The heap stores data whose type and size we do not know at compile time and which may change during runtime (assigning other values to the properties of objects, creating new properties, new objects and so on), so the heap is dynamic memory. Technically speaking, the heap is an implementation of the common design pattern of dynamic memory allocation, which means that it requires a Garbage Collector, which cleans up allocated memory during runtime according to whether some piece of the program still needs (holds references in the call stack to) the objects allocated on the heap. If not, the Garbage Collector under normal circumstances cleans the heap and frees the memory. The Garbage Collector is not 100% accurate, though, so in spite of its existence memory leaks can still occur.\u003C\u002Fp>\n\u003Ch3>Call Stack\u003C\u002Fh3>\n\u003Cp>The \u003Cstrong>call stack\u003C\u002Fstrong>, on the other hand, is a simpler, static memory and far more interesting for explaining the Event Loop, because it has to do with the order in which code is executed in Javascript. The Javascript engine works with the call stack only synchronously, step by step. Technically, the call stack is an implementation of the classic \u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FStack_(abstract_data_type)\">Stack design pattern\u003C\u002Fa>, in which the element added to memory last is processed first (Last In, First Out, LIFO). And what exactly is stored in the call stack, and the order of \u003Cstrong>what\u003C\u002Fstrong> exactly does it determine? To keep things simple, we can picture a Javascript program as a collection of functions and variables, and the call stack makes sure that our functions are called and our variables declared in the right order and at the right time.\u003C\u002Fp>\n\u003Cp>While complex data types (objects, arrays and so on) are stored on the heap, the call stack holds only references\u002Fpointers into the heap. What the call stack really does store are the values of primitive data types (string, integer, boolean and so on), and also the whole \u003Cstrong>execution context\u003C\u002Fstrong> of a function, by which we mean everything “that is needed to call the function”. Again, it would be appropriate to dive deeper into everything a function’s execution context means, but to keep things simple, the execution context includes:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Variable Environment\u003C\u002Fstrong>: A record of the variables and function declarations specific to the given function.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Lexical Environment\u003C\u002Fstrong>: References to the parent scope (lexical environment) and the variables defined with let and const.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>The this binding\u003C\u002Fstrong>: The value of this in the current function.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Specific details of the call\u003C\u002Fstrong>: The arguments passed to the function and the mechanism of its return (that is, the place in the program to which the program should return once the function has finished).\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>If I let Claude generate a random example to show how the call stack works, let us take, say, the following code:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-js\">function multiply(x, y) {\n  const result = x * y;\n  return result;\n}\n\nfunction calculate() {\n  const a = 10;\n  const b = 20;\n  const product = multiply(a, b);\n  console.log(product);\n}\n\ncalculate();\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>What exactly happens in such a simple example? The situation is as follows: first the function \u003Ccode>calculate()\u003C\u002Fcode>, or rather its whole execution context, is put on the \u003Cstrong>call stack\u003C\u002Fstrong>. Then the execution context of the function \u003Ccode>multiply()\u003C\u002Fcode> is added to the \u003Cstrong>call stack\u003C\u002Fstrong>. As soon as \u003Ccode>multiply()\u003C\u002Fcode> has been called and its result assigned to the variable \u003Ccode>product\u003C\u002Fcode>, the function \u003Ccode>multiply()\u003C\u002Fcode> and its execution context are removed from the call stack. Then the execution context of \u003Ccode>console.log()\u003C\u002Fcode> is put on the call stack. After the value “200” has been printed to the browser console, the execution context of \u003Ccode>console.log()\u003C\u002Fcode> is removed from the call stack. Finally the execution context of the function \u003Ccode>calculate()\u003C\u002Fcode> is removed as well.\u003C\u002Fp>\n\u003Cp>Notice that the example contains no global variables, and that the call of the function calculate() itself is seemingly not inside any function whose execution context could be added to the call stack. So how do global variables and function calls in the global scope get onto the call stack?\u003C\u002Fp>\n\u003Ch4>Global Execution Context (GEC)\u003C\u002Fh4>\n\u003Cp>Everything in the global scope can be understood as being called inside one main function, whose execution context has a special name and a special behaviour: the \u003Cstrong>global execution context\u003C\u002Fstrong>. Let us modify the example above and add global variables to it.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-js\">var globalA = 'testGlobalA';\nconst globalB = 'testGlobalB';\n\nfunction multiply(x, y) {\n  const result = x * y;\n  return result;\n}\n\nfunction calculate() {\n  const a = 10;\n  const b = 20;\n  const product = multiply(a, b);\n  console.log(product);\n}\n\ncalculate();\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>If we accept that the global context of our scripts is treated as a kind of “main” function, our script will behave as follows: the global execution context is added to the call stack, the variable “globalA” is added to the \u003Cstrong>variable environment\u003C\u002Fstrong>, the variable “globalB” is added \u003Cstrong>to the lexical environment\u003C\u002Fstrong>, and a \u003Cstrong>this binding\u003C\u002Fstrong> is created, which in the browser environment will refer to the global object \u003Cstrong>window\u003C\u002Fstrong>. As for the \u003Cstrong>window\u003C\u002Fstrong> object, it is worth recalling that while \u003Cstrong>var globalA\u003C\u002Fstrong> is added as a property to the \u003Cstrong>window\u003C\u002Fstrong> object, variables defined with \u003Cstrong>const\u003C\u002Fstrong> or \u003Cstrong>let\u003C\u002Fstrong> are added to the global scope\u002Fglobal lexical environment, but not to the global object \u003Cstrong>window\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>Once all the functions defined “inside” the global execution context have been added to the call stack, processed and removed from it, only the global execution context remains on the call stack as the last function; in the end it, too, is removed, and the call stack is empty.\u003C\u002Fp>\n\u003Cp>That, at least, is how it works in principle, as long as all the code the Javascript engine processes is \u003Cstrong>synchronous\u003C\u002Fstrong>. If our application (read: inside the global execution context) contains functions that are processed asynchronously, the situation is considerably different.\u003C\u002Fp>\n\u003Cp>We said that the \u003Cstrong>global execution context\u003C\u002Fstrong> (GEC) is a kind of primary “main” function and the entry point of the application. It seems that it has to run the whole time, until all the code has been processed. But what about the asynchronous parts of the code? They can, after all, start further functions that return new values and create new execution contexts, so the global execution context has to be present on the call stack “somehow”. And how and when is asynchronous code called when the call stack is partly full, or when it is completely empty? And if the call stack is empty, how is it possible for anything to run in Javascript at all, for example when a user clicks a button and the callback of an event listener registered with addEventListener() is run? And what monitors the asynchronous callbacks of addEventListener, when the Javascript engine is entirely synchronous and does not even contain any addEventListener function?\u003C\u002Fp>\n\u003Ch2>Asynchrony in Javascript, or Web APIs, Task queues, Event loop\u003C\u002Fh2>\n\u003Cp>The engine of the Javascript programming language contains neither the \u003Ccode>console\u003C\u002Fcode> object nor its method \u003Ccode>console.log()\u003C\u002Fcode>. Which may come as a surprise. The fact is that functionality programmers take for granted, such as \u003Ccode>setTimeout\u003C\u002Fcode>, \u003Ccode>addEventListener\u003C\u002Fcode>, the operations for manipulating the \u003Ccode>DOM\u003C\u002Fcode>, or the asynchronous network requests \u003Ccode>fetch\u003C\u002Fcode> and \u003Ccode>XMLHttpRequest\u003C\u002Fcode>, is part not of the Javascript engine but of another component of the Javascript runtime: the Web API.\u003C\u002Fp>\n\u003Ch3>Web API\u003C\u002Fh3>\n\u003Cp>How exactly does the Javascript engine manage to cooperate with the Web API so that the Engine has access to the functions \u003Ccode>console.log()\u003C\u002Fcode> or \u003Ccode>setTimeout()\u003C\u002Fcode>, if they are not directly part of the engine? A Javascript Engine (V8, for example) has an \u003Cstrong>internal registry of native functions\u003C\u002Fstrong>, which are defined in the ECMAScript specification (e.g. Math.random(), Array.prototype.map(), Date.now()). From it the Engine recognises that \u003Ccode>setTimeout()\u003C\u002Fcode> is not a native function, and so it reaches into the global scope, or rather the \u003Ccode>window\u003C\u002Fcode> object, and tries to call the method \u003Ccode>window.setTimeout()\u003C\u002Fcode>. For even before the program is initialised, the Javascript runtime makes sure that the global execution context (and therefore the lexical environment of every function, see above) contains the objects that are part of the Web API, e.g. document, navigator, performance, Intl, crypto, console.\u003C\u002Fp>\n\u003Cp>When I asked ChatGPT to explain to me how the method \u003Ccode>console.log()\u003C\u002Fcode> gets run, it wrote, wrongly, that \u003Ccode>console.log()\u003C\u002Fcode> is part of the Javascript engine itself. When I pointed out that this is not so, ChatGPT apologised and offered me a more accurate answer. I was curious why it had made the mistake. It blamed it on a mental model of how Javascript works that is supposedly widespread among developers (and therefore among bloggers too), in which \u003Ccode>console.log()\u003C\u002Fcode> is a synchronous operation and is thus processed directly on the call stack (which is true), and from this it follows that \u003Cstrong>if something is processed directly on the call stack (and needs neither callbacks, which are processed asynchronously off Javascript’s main thread, nor therefore the Event loop), it must be part of the Javascript engine.\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>That is a mistake. The method \u003Ccode>console.log()\u003C\u002Fcode> does not exist in the Javascript engine; the engine knows nothing about it, except that it is not a native function. It hands its actual processing over to the Javascript runtime, where the browser (or nodejs) then carries the function out with low-level code in C++ by means of C++ bindings, which are the relations between a Javascript function and the C++ function that really does what we want from the Javascript function.\u003C\u002Fp>\n\u003Cp>We now know roughly how it works when the Javascript engine needs to call a non-native function from the Web API. But what about asynchrony and the calls to \u003Ccode>setTimeout()\u003C\u002Fcode>, \u003Ccode>addEventListener()\u003C\u002Fcode> or \u003Ccode>fetch()\u003C\u002Fcode>?\u003C\u002Fp>\n\u003Ch3>Callbacks and Task Queues in general\u003C\u002Fh3>\n\u003Cp>As a single-threaded language, the Javascript engine needs to delegate the execution of asynchronous operations outside itself. Besides the Web API, the Javascript runtime contains further components that make asynchronous operations possible: the Task queues and the Event loop. All asynchronous operations in Javascript are handled by means of callbacks (and that includes \u003Ccode>await\u002Fasync\u003C\u002Fcode>, which is sugar code that uses Promise-based asynchronicity). When the Engine comes across an asynchronous operation, it hands its processing over entirely to the Web API. If it is a timer such as \u003Ccode>setTimeout()\u003C\u002Fcode>, the Web API waits the given number of milliseconds; if it is a network request such as \u003Ccode>fetch\u003C\u002Fcode>, it is again up to the Web API to wait for the response to that network request. As soon as the Web API has finished processing the asynchronous operation itself, it takes the \u003Cstrong>callback\u003C\u002Fstrong> defined by the programmer and passes this callback function to a special component of the Javascript runtime, a queue called the \u003Cstrong>Task queue\u003C\u002Fstrong> or the \u003Cstrong>Microtask\u003C\u002Fstrong> \u003Cstrong>queue\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>The callbacks stay in the queues until the call stack is completely empty, or rather until only the global execution context (GEC) remains in it. As soon as only the GEC is on the call stack, the next component takes its turn: the Event Loop.\u003C\u002Fp>\n\u003Ch3>Task queue\u003C\u002Fh3>\n\u003Cp>The Task queue is the older of the two queues, and in a sense it is as old as Javascript itself. The very first versions of Javascript in Netscape Navigator already knew \u003Ccode>setTimeout()\u003C\u002Fcode> and could react to events raised by the user, and both needed a place where a callback waits until the engine finishes what it is doing at the moment. Formally, though, the Task queue (and the Event loop altogether) was first described by the HTML5 specification of the WHATWG organisation, which unified how all browsers are to behave in this respect.\u003C\u002Fp>\n\u003Cp>The reason the Task queue came into being is the one we started with: a single-threaded engine must not wait for anything. The Web API therefore deals with the waiting outside the engine and then hands to the Task queue only the finished callback, which is processed when its turn comes. The Task queue (the HTML specification calls them \u003Cstrong>tasks\u003C\u002Fstrong>; in blogs we often also meet the name \u003Cstrong>macrotasks\u003C\u002Fstrong>) is the place for:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>the callbacks of the timers \u003Ccode>setTimeout()\u003C\u002Fcode> and \u003Ccode>setInterval()\u003C\u002Fcode>,\u003C\u002Fli>\n\u003Cli>the callbacks of events registered with \u003Ccode>addEventListener()\u003C\u002Fcode> (a click, a key press, a scroll…),\u003C\u002Fli>\n\u003Cli>the network events of older APIs, for example \u003Ccode>onload\u003C\u002Fcode> on \u003Ccode>XMLHttpRequest\u003C\u002Fcode> or messages from a \u003Ccode>WebSocket\u003C\u002Fcode>,\u003C\u002Fli>\n\u003Cli>messages between windows and workers sent with \u003Ccode>postMessage()\u003C\u002Fcode>,\u003C\u002Fli>\n\u003Cli>and indeed the very running of a script from a \u003Ccode>&lt;script&gt;\u003C\u002Fcode> tag – so our global execution context, too, begins its life as a task.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Two things are worth mentioning. First, “Task queue” in the singular is a simplification. The specification allows a browser to have several queues (for example one for user interactions and another for timers) and to choose among them, so a browser may give a click priority over a timer. Within a single queue, however, the FIFO method applies. Second, \u003Cstrong>the Event loop always takes just one task from the Task queue\u003C\u002Fstrong>, and between two tasks the browser has an opportunity to repaint the page. That is why even \u003Ccode>setTimeout(callback, 0)\u003C\u002Fcode> does not mean “right away” but “at the earliest in the next turn of the Event loop”. With deeply nested timer calls, moreover, browsers raise the minimum delay to 4 ms.\u003C\u002Fp>\n\u003Ch3>Microtask queue\u003C\u002Fh3>\n\u003Cp>The Microtask queue is a good deal younger. The term \u003Cstrong>microtask\u003C\u002Fstrong> appeared around 2011, when engineers from Mozilla and Chromium were designing a replacement for the so-called Mutation Events – an old mechanism for watching changes in the DOM. Mutation Events fired synchronously on every single change to the DOM, which was slow and error-prone. The new \u003Ccode>MutationObserver\u003C\u002Fcode> API was meant to deliver changes asynchronously and all at once in a batch, yet still before the browser repaints the page or processes the next event. The Task queue was not enough for that, because between two tasks a repaint or the processing of another click can already happen. And so a new queue came into being, whose callbacks are processed \u003Cstrong>as soon as the call stack empties, still before the next task\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>The second and today the best-known use of the Microtask queue became \u003Cstrong>Promises\u003C\u002Fstrong>, standardised in ECMAScript 2015 (ES6). The ECMAScript specification calls them “jobs”, but the browser puts them precisely into the Microtask queue. Promises thereby received an important guarantee: the callback in \u003Ccode>.then()\u003C\u002Fcode> is never called synchronously, but not needlessly late either – always right after the currently running code has finished. Around 2019 browsers then added the function \u003Ccode>queueMicrotask()\u003C\u002Fcode>, with which the programmer, too, can schedule a microtask directly, without the detour through a Promise.\u003C\u002Fp>\n\u003Cp>The Microtask queue is the place for:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Promise callbacks: \u003Ccode>.then()\u003C\u002Fcode>, \u003Ccode>.catch()\u003C\u002Fcode>, \u003Ccode>.finally()\u003C\u002Fcode>,\u003C\u002Fli>\n\u003Cli>the continuation of an async function after \u003Ccode>await\u003C\u002Fcode> (async\u002Fawait is built on top of Promises),\u003C\u002Fli>\n\u003Cli>callbacks passed to \u003Ccode>queueMicrotask()\u003C\u002Fcode>,\u003C\u002Fli>\n\u003Cli>\u003Ccode>MutationObserver\u003C\u002Fcode> callbacks.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>And one more important rule: while the Event loop takes exactly one task from the Task queue at a given moment, \u003Cstrong>it always empties the whole Microtask queue\u003C\u002Fstrong>, including the microtasks that were added while it was being processed. If some microtask kept adding another microtask, the browser would never get to the click or to repainting the page, and the page would freeze – just as with an infinite loop in synchronous code.\u003C\u002Fp>\n\u003Ch2>Event Loop\u003C\u002Fh2>\n\u003Cp>The mysterious Event loop now turns out to be something relatively trivial. The Event loop is nothing more than one component of the Javascript runtime that keeps checking whether the call stack is full. If it is empty, the Event loop’s next job is to decide whether to take, by the FIFO method, the first callback from the Task queue or from the Microtask queue. That is all.\u003C\u002Fp>\n\u003Csvg id=\"el-anim\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" viewBox=\"0 0 480 764\" role=\"img\" aria-label=\"Animation: how the Event Loop processes the opening example\" aria-describedby=\"el-anim-desc\" style=\"display:block;width:100%;max-width:560px;height:auto;margin:2.5rem auto\">\n\u003Cdesc id=\"el-anim-desc\">The script runs in the global execution context. setTimeout hands a timer to the Web API and, after 0 ms, its callback is placed in the Task queue. The callback from .then() of the fulfilled Promise is placed in the Microtask queue. console.log runs synchronously and prints “hello from console log” to the console. When the call stack empties, the Event Loop first processes the Microtask queue and prints “ahoy from promise”, and only then takes the task from the Task queue and prints “hey, from set timeout”.\u003C\u002Fdesc>\n\u003Cstyle>\n#el-anim{font-family:\"EB Garamond\",Georgia,serif}\n#el-anim .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;fill:#1a1a1a}\n#el-anim .code{font-size:12px}\n#el-anim .ui{font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif;font-size:11px;fill:#5f6368}\n#el-anim .panel{fill:#fafafa;stroke:#d0d0d0;stroke-width:1.5}\n#el-anim .label{font-size:16px;font-weight:600;fill:#1a1a1a}\n#el-anim .hint{font-size:12px;fill:#8a8a8a}\n#el-anim .cap text{font-size:15px;fill:#1a1a1a}\n#el-anim .frame rect{fill:#eeeeee;stroke:#8a8a8a;stroke-width:1.2}\n#el-anim .cb rect{fill:#e6f0e6;stroke:#006400;stroke-width:1.2}\n#el-anim .timer rect{fill:#fff6db;stroke:#b8860b;stroke-width:1.2}\n#el-anim .hl{fill:#fff1a8}\n#el-anim .rule{stroke:#e8eaed;stroke-width:1}\n#el-anim .anim{animation-duration:30s;animation-iteration-count:infinite;animation-timing-function:ease-in-out;animation-fill-mode:both}\n#el-anim .btn{cursor:pointer}\n#el-anim .btn:focus{outline:none}\n#el-anim .btn rect{fill:#ffffff;stroke:#c8c8c8;stroke-width:1.2}\n#el-anim .btn:hover rect,#el-anim .btn:focus-visible rect{fill:#eef6ee;stroke:#006400}\n#el-anim .btn text{font-size:14px;fill:#1a1a1a}\n#el-anim .btn .ico{fill:#1a1a1a}\n#el-anim .tok,#el-anim .hl,#el-anim .cap{opacity:0}\n#el-anim .out,#el-anim .cap-end{opacity:1}\n#el-anim .loop{color:#9a9a9a}\n#el-anim .spin{transform-box:fill-box;transform-origin:center;animation:elAnim-spin 3s linear infinite}\n#el-anim .caret{animation:elAnim-blink 1.1s steps(1) infinite}\n#el-anim .g-global{animation-name:elAnim-global}\n#el-anim .g-set{animation-name:elAnim-set}\n#el-anim .g-timer{animation-name:elAnim-timer}\n#el-anim .g-cb1{animation-name:elAnim-cb1}\n#el-anim .g-prom{animation-name:elAnim-prom}\n#el-anim .g-cb2{animation-name:elAnim-cb2}\n#el-anim .g-log{animation-name:elAnim-log}\n#el-anim .g-in2{animation-name:elAnim-in2}\n#el-anim .g-in3{animation-name:elAnim-in3}\n#el-anim .hl1{animation-name:elAnim-hl1}\n#el-anim .hl2{animation-name:elAnim-hl2}\n#el-anim .hl3{animation-name:elAnim-hl3}\n#el-anim .hl4{animation-name:elAnim-in3}\n#el-anim .hl5{animation-name:elAnim-in2}\n#el-anim .out1{animation-name:elAnim-out1}\n#el-anim .out2{animation-name:elAnim-out2}\n#el-anim .out3{animation-name:elAnim-out3}\n#el-anim .loop{animation-name:elAnim-loop}\n#el-anim .mlane{animation-name:elAnim-mlane}\n#el-anim .tlane{animation-name:elAnim-tlane}\n#el-anim .c1{animation-name:elAnim-c1}\n#el-anim .c2{animation-name:elAnim-c2}\n#el-anim .c3{animation-name:elAnim-c3}\n#el-anim .c4{animation-name:elAnim-c4}\n#el-anim .c5{animation-name:elAnim-c5}\n#el-anim .c6{animation-name:elAnim-c6}\n#el-anim .c7{animation-name:elAnim-c7}\n#el-anim .c8{animation-name:elAnim-c8}\n@keyframes elAnim-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}\n@keyframes elAnim-blink{0%{opacity:1}50%{opacity:0}}\n@keyframes elAnim-global{0%{opacity:0}2%,50%{opacity:1}52%,100%{opacity:0}}\n@keyframes elAnim-set{0%,8%{opacity:0}9%,16%{opacity:1}18%,100%{opacity:0}}\n@keyframes elAnim-timer{0%,12%{opacity:0}13%,18%{opacity:1}20%,100%{opacity:0}}\n@keyframes elAnim-cb1{0%,19%{opacity:0;transform:translate(258px,468px)}20%{opacity:1;transform:translate(258px,468px)}23%,78%{opacity:1;transform:translate(26px,468px)}82%,90%{opacity:1;transform:translate(26px,320px)}92%,100%{opacity:0;transform:translate(26px,320px)}}\n@keyframes elAnim-prom{0%,24%{opacity:0}25%,33%{opacity:1}35%,100%{opacity:0}}\n@keyframes elAnim-cb2{0%,30%{opacity:0;transform:translate(258px,398px)}31%{opacity:1;transform:translate(258px,398px)}34%,60%{opacity:1;transform:translate(26px,398px)}64%,72%{opacity:1;transform:translate(26px,320px)}74%,100%{opacity:0;transform:translate(26px,320px)}}\n@keyframes elAnim-log{0%,38%{opacity:0}39%,46%{opacity:1}48%,100%{opacity:0}}\n@keyframes elAnim-in2{0%,65%{opacity:0}66%,70%{opacity:1}72%,100%{opacity:0}}\n@keyframes elAnim-in3{0%,83%{opacity:0}84%,88%{opacity:1}90%,100%{opacity:0}}\n@keyframes elAnim-hl1{0%,8%{opacity:0}9%,16%{opacity:1}18%,100%{opacity:0}}\n@keyframes elAnim-hl2{0%,24%{opacity:0}25%,33%{opacity:1}35%,100%{opacity:0}}\n@keyframes elAnim-hl3{0%,38%{opacity:0}39%,46%{opacity:1}48%,100%{opacity:0}}\n@keyframes elAnim-out1{0%,42%{opacity:0}43%,97%{opacity:1}100%{opacity:0}}\n@keyframes elAnim-out2{0%,67%{opacity:0}68%,97%{opacity:1}100%{opacity:0}}\n@keyframes elAnim-out3{0%,85%{opacity:0}86%,97%{opacity:1}100%{opacity:0}}\n@keyframes elAnim-loop{0%,53%{color:#9a9a9a}55%,91%{color:#006400}93%,100%{color:#9a9a9a}}\n@keyframes elAnim-mlane{0%,55%{fill:#fafafa;stroke:#d0d0d0}56%,64%{fill:#eef6ee;stroke:#006400}66%,100%{fill:#fafafa;stroke:#d0d0d0}}\n@keyframes elAnim-tlane{0%,73%{fill:#fafafa;stroke:#d0d0d0}74%,82%{fill:#eef6ee;stroke:#006400}84%,100%{fill:#fafafa;stroke:#d0d0d0}}\n@keyframes elAnim-c1{0%,7%{opacity:1}8%,100%{opacity:0}}\n@keyframes elAnim-c2{0%,7%{opacity:0}8%,23%{opacity:1}24%,100%{opacity:0}}\n@keyframes elAnim-c3{0%,23%{opacity:0}24%,37%{opacity:1}38%,100%{opacity:0}}\n@keyframes elAnim-c4{0%,37%{opacity:0}38%,49%{opacity:1}50%,100%{opacity:0}}\n@keyframes elAnim-c5{0%,49%{opacity:0}50%,55%{opacity:1}56%,100%{opacity:0}}\n@keyframes elAnim-c6{0%,55%{opacity:0}56%,73%{opacity:1}74%,100%{opacity:0}}\n@keyframes elAnim-c7{0%,73%{opacity:0}74%,91%{opacity:1}92%,100%{opacity:0}}\n@keyframes elAnim-c8{0%,91%{opacity:0}92%,99%{opacity:1}100%{opacity:0}}\n@media (prefers-reduced-motion:reduce){#el-anim .anim,#el-anim .spin,#el-anim .caret{animation:none}#el-anim .controls{display:none}}\n\u003C\u002Fstyle>\n\u003Crect class=\"panel\" x=\"16\" y=\"16\" width=\"448\" height=\"140\" rx=\"10\"\u002F>\n\u003Crect class=\"hl hl1 anim\" x=\"20\" y=\"26\" width=\"440\" height=\"60\" rx=\"4\"\u002F>\n\u003Crect class=\"hl hl2 anim\" x=\"20\" y=\"86\" width=\"440\" height=\"40\" rx=\"4\"\u002F>\n\u003Crect class=\"hl hl3 anim\" x=\"20\" y=\"126\" width=\"440\" height=\"20\" rx=\"4\"\u002F>\n\u003Crect class=\"hl hl4 anim\" x=\"20\" y=\"46\" width=\"440\" height=\"20\" rx=\"4\"\u002F>\n\u003Crect class=\"hl hl5 anim\" x=\"20\" y=\"106\" width=\"440\" height=\"20\" rx=\"4\"\u002F>\n\u003Ctext class=\"mono code\" x=\"28\" y=\"40\">setTimeout(() =&gt; {\u003C\u002Ftext>\n\u003Ctext class=\"mono code\" x=\"42.4\" y=\"60\">console.log('hey, from set timeout');\u003C\u002Ftext>\n\u003Ctext class=\"mono code\" x=\"28\" y=\"80\">}, 0);\u003C\u002Ftext>\n\u003Ctext class=\"mono code\" x=\"28\" y=\"100\">new Promise((resolve) =&gt; resolve('ahoy from promise'))\u003C\u002Ftext>\n\u003Ctext class=\"mono code\" x=\"42.4\" y=\"120\">.then((v) =&gt; console.log(v));\u003C\u002Ftext>\n\u003Ctext class=\"mono code\" x=\"28\" y=\"140\">console.log('hello from console log');\u003C\u002Ftext>\n\u003Crect class=\"panel\" x=\"16\" y=\"170\" width=\"216\" height=\"190\" rx=\"10\"\u002F>\n\u003Ctext class=\"label\" x=\"28\" y=\"192\">Call stack\u003C\u002Ftext>\n\u003Crect class=\"panel\" x=\"248\" y=\"170\" width=\"216\" height=\"90\" rx=\"10\"\u002F>\n\u003Ctext class=\"label\" x=\"260\" y=\"192\">Web APIs\u003C\u002Ftext>\n\u003Crect class=\"panel\" x=\"248\" y=\"272\" width=\"216\" height=\"88\" rx=\"10\"\u002F>\n\u003Cg class=\"loop anim\">\n\u003Cg class=\"spin\">\n\u003Ccircle cx=\"276\" cy=\"318\" r=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-dasharray=\"90 23\"\u002F>\n\u003Cpolygon points=\"286.9,302.4 282.4,296.4 279.8,305.0\" fill=\"currentColor\"\u002F>\n\u003C\u002Fg>\n\u003Ctext x=\"306\" y=\"306\" style=\"font-size:18px;font-weight:600\" fill=\"currentColor\">Event Loop\u003C\u002Ftext>\n\u003C\u002Fg>\n\u003Ctext class=\"hint\" x=\"306\" y=\"325\">empty stack? → first\u003C\u002Ftext>\n\u003Ctext class=\"hint\" x=\"306\" y=\"340\">all microtasks, then a task\u003C\u002Ftext>\n\u003Crect class=\"panel mlane anim\" x=\"16\" y=\"376\" width=\"448\" height=\"58\" rx=\"10\"\u002F>\n\u003Ctext class=\"label\" x=\"28\" y=\"392\" style=\"font-size:15px\">Microtask queue\u003C\u002Ftext>\n\u003Ctext class=\"hint\" x=\"454\" y=\"392\" text-anchor=\"end\">Promise, queueMicrotask, MutationObserver\u003C\u002Ftext>\n\u003Crect class=\"panel tlane anim\" x=\"16\" y=\"446\" width=\"448\" height=\"58\" rx=\"10\"\u002F>\n\u003Ctext class=\"label\" x=\"28\" y=\"462\" style=\"font-size:15px\">Task queue\u003C\u002Ftext>\n\u003Ctext class=\"hint\" x=\"454\" y=\"462\" text-anchor=\"end\">setTimeout, events, network operations\u003C\u002Ftext>\n\u003Crect x=\"16\" y=\"520\" width=\"448\" height=\"144\" rx=\"8\" fill=\"#ffffff\" stroke=\"#cfd1d4\" stroke-width=\"1.5\"\u002F>\n\u003Cpath d=\"M16 546 V528 a8 8 0 0 1 8 -8 H456 a8 8 0 0 1 8 8 V546 Z\" fill=\"#f1f3f4\"\u002F>\n\u003Ctext class=\"ui\" x=\"28\" y=\"537\">Elements\u003C\u002Ftext>\n\u003Ctext class=\"ui\" x=\"90\" y=\"537\" style=\"fill:#202124\">Console\u003C\u002Ftext>\n\u003Ctext class=\"ui\" x=\"146\" y=\"537\">Sources\u003C\u002Ftext>\n\u003Ctext class=\"ui\" x=\"199\" y=\"537\">Network\u003C\u002Ftext>\n\u003Crect x=\"84\" y=\"543\" width=\"52\" height=\"3\" fill=\"#1a73e8\"\u002F>\n\u003Cline class=\"rule\" x1=\"16\" y1=\"546\" x2=\"464\" y2=\"546\"\u002F>\n\u003Ccircle cx=\"31\" cy=\"558\" r=\"5.5\" fill=\"none\" stroke=\"#5f6368\" stroke-width=\"1.2\"\u002F>\n\u003Cline x1=\"27.1\" y1=\"561.9\" x2=\"34.9\" y2=\"554.1\" stroke=\"#5f6368\" stroke-width=\"1.2\"\u002F>\n\u003Ctext class=\"ui\" x=\"46\" y=\"562\">top ▾\u003C\u002Ftext>\n\u003Crect x=\"84\" y=\"551\" width=\"150\" height=\"15\" rx=\"3\" fill=\"#ffffff\" stroke=\"#dadce0\"\u002F>\n\u003Ctext class=\"ui\" x=\"90\" y=\"562\" style=\"fill:#9aa0a6\">Filter\u003C\u002Ftext>\n\u003Ctext class=\"ui\" x=\"454\" y=\"562\" text-anchor=\"end\">Default levels ▾\u003C\u002Ftext>\n\u003Cline class=\"rule\" x1=\"16\" y1=\"570\" x2=\"464\" y2=\"570\"\u002F>\n\u003Cg class=\"out out1 anim\">\u003Ctext class=\"mono\" x=\"28\" y=\"584\">hello from console log\u003C\u002Ftext>\u003Ctext class=\"ui\" x=\"454\" y=\"584\" text-anchor=\"end\" style=\"text-decoration:underline\">script.js:6\u003C\u002Ftext>\u003Cline class=\"rule\" x1=\"16\" y1=\"590\" x2=\"464\" y2=\"590\"\u002F>\u003C\u002Fg>\n\u003Cg class=\"out out2 anim\">\u003Ctext class=\"mono\" x=\"28\" y=\"604\">ahoy from promise\u003C\u002Ftext>\u003Ctext class=\"ui\" x=\"454\" y=\"604\" text-anchor=\"end\" style=\"text-decoration:underline\">script.js:5\u003C\u002Ftext>\u003Cline class=\"rule\" x1=\"16\" y1=\"610\" x2=\"464\" y2=\"610\"\u002F>\u003C\u002Fg>\n\u003Cg class=\"out out3 anim\">\u003Ctext class=\"mono\" x=\"28\" y=\"624\">hey, from set timeout\u003C\u002Ftext>\u003Ctext class=\"ui\" x=\"454\" y=\"624\" text-anchor=\"end\" style=\"text-decoration:underline\">script.js:2\u003C\u002Ftext>\u003Cline class=\"rule\" x1=\"16\" y1=\"630\" x2=\"464\" y2=\"630\"\u002F>\u003C\u002Fg>\n\u003Ctext x=\"26\" y=\"646\" style=\"font-size:14px;font-weight:700\" fill=\"#1a73e8\">›\u003C\u002Ftext>\n\u003Crect class=\"caret\" x=\"38\" y=\"636\" width=\"1.2\" height=\"12\" fill=\"#202124\"\u002F>\n\u003Cg class=\"tok frame g-global anim\" transform=\"translate(26 320)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">global execution context\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok frame g-set anim\" transform=\"translate(26 284)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">setTimeout(…, 0)\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok timer g-timer anim\" transform=\"translate(258 208)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">timer: 0 ms\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok frame g-prom anim\" transform=\"translate(26 284)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">new Promise(…).then(…)\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok frame g-log anim\" transform=\"translate(26 284)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">console.log('hello…')\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok frame g-in2 anim\" transform=\"translate(26 284)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">console.log(v)\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok frame g-in3 anim\" transform=\"translate(26 284)\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">console.log('hey…')\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok cb g-cb2 anim\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">(v) =&gt; console.log(v)\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"tok cb g-cb1 anim\">\u003Crect width=\"196\" height=\"28\" rx=\"6\"\u002F>\u003Ctext class=\"mono\" x=\"10\" y=\"18.5\">() =&gt; { console.log(…) }\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c1 anim\">\u003Ctext x=\"16\" y=\"690\">1. The script starts as a task. The global execution\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">context is put on the call stack.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c2 anim\">\u003Ctext x=\"16\" y=\"690\">2. setTimeout() hands a timer to the Web API. After\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">0 ms its callback is placed in the Task queue.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c3 anim\">\u003Ctext x=\"16\" y=\"690\">3. The Promise is fulfilled at once, so the callback\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">from .then() heads for the Microtask queue.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c4 anim\">\u003Ctext x=\"16\" y=\"690\">4. console.log() runs synchronously, right on the call\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">stack, and prints the first line to the console.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c5 anim\">\u003Ctext x=\"16\" y=\"690\">5. The script has finished and the call stack is empty.\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">The Event Loop takes over.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c6 anim\">\u003Ctext x=\"16\" y=\"690\">6. The Event Loop first empties the Microtask queue:\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">the callback from .then() prints “ahoy from promise”.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap c7 anim\">\u003Ctext x=\"16\" y=\"690\">7. The Microtask queue is empty, so the Event Loop\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">takes the first task: the callback from setTimeout().\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"cap cap-end c8 anim\">\u003Ctext x=\"16\" y=\"690\">Result: “hello”, “ahoy”, “hey” – synchronous code first,\u003C\u002Ftext>\u003Ctext x=\"16\" y=\"710\">then all the microtasks, and only then the tasks.\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"controls\">\n\u003Cg class=\"btn\" role=\"button\" tabindex=\"0\" aria-label=\"Play the animation from the start\" onclick=\"this.ownerSVGElement.getAnimations({subtree:true}).forEach(function(a){a.currentTime=0;a.play()})\" onkeydown=\"if(event.key==='Enter'||event.key===' '){event.preventDefault();this.onclick()}\">\u003Crect x=\"16\" y=\"731\" width=\"112\" height=\"24\" rx=\"12\"\u002F>\u003Crect class=\"ico\" x=\"27\" y=\"737\" width=\"2\" height=\"12\"\u002F>\u003Cpolygon class=\"ico\" points=\"37,737 37,749 30,743\"\u002F>\u003Ctext x=\"44\" y=\"747\">Restart\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"btn\" role=\"button\" tabindex=\"0\" aria-label=\"Pause the animation\" onclick=\"this.ownerSVGElement.getAnimations({subtree:true}).forEach(function(a){a.pause()})\" onkeydown=\"if(event.key==='Enter'||event.key===' '){event.preventDefault();this.onclick()}\">\u003Crect x=\"136\" y=\"731\" width=\"96\" height=\"24\" rx=\"12\"\u002F>\u003Crect class=\"ico\" x=\"147\" y=\"737\" width=\"3\" height=\"12\"\u002F>\u003Crect class=\"ico\" x=\"153\" y=\"737\" width=\"3\" height=\"12\"\u002F>\u003Ctext x=\"164\" y=\"747\">Pause\u003C\u002Ftext>\u003C\u002Fg>\n\u003Cg class=\"btn\" role=\"button\" tabindex=\"0\" aria-label=\"Resume the animation\" onclick=\"this.ownerSVGElement.getAnimations({subtree:true}).forEach(function(a){a.play()})\" onkeydown=\"if(event.key==='Enter'||event.key===' '){event.preventDefault();this.onclick()}\">\u003Crect x=\"240\" y=\"731\" width=\"112\" height=\"24\" rx=\"12\"\u002F>\u003Cpolygon class=\"ico\" points=\"251,737 251,749 260,743\"\u002F>\u003Ctext x=\"266\" y=\"747\">Resume\u003C\u002Ftext>\u003C\u002Fg>\n\u003C\u002Fg>\n\u003C\u002Fsvg>\n",1790905667527]