<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marsha Teo</title>
    <description>The latest articles on DEV Community by Marsha Teo (@marshateo).</description>
    <link>https://dev.to/marshateo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3862667%2F204e9381-60ad-4ef3-bd15-5d4a3bdcfb60.png</url>
      <title>DEV Community: Marsha Teo</title>
      <link>https://dev.to/marshateo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marshateo"/>
    <language>en</language>
    <item>
      <title>The JavaScript Runtime: Fixing the Mental Model</title>
      <dc:creator>Marsha Teo</dc:creator>
      <pubDate>Fri, 10 Apr 2026 20:36:36 +0000</pubDate>
      <link>https://dev.to/marshateo/the-javascript-runtime-fixing-the-mental-model-5f5b</link>
      <guid>https://dev.to/marshateo/the-javascript-runtime-fixing-the-mental-model-5f5b</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is part of a &lt;a href="https://dev.to/marshateo/javascript-event-loop-series-building-the-event-loop-mental-model-from-experiments-4d8i"&gt;series on the JavaScript event loop&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Most explanations of JavaScript's event loop start with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;JavaScript is single-threaded.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This statement is technically true but doesn't explain certain behaviors in JavaScript you may have noticed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;setTimeout&lt;/code&gt; doesn't interrupt loops after the timer has run out&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setTimeout&lt;/code&gt; doesn't block (like &lt;code&gt;sleep(1)&lt;/code&gt; in C)&lt;/li&gt;
&lt;li&gt;A resolved &lt;code&gt;Promise&lt;/code&gt; still runs after synchronous code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;await&lt;/code&gt; pauses a function but doesn't freeze the page&lt;/li&gt;
&lt;li&gt;Rendering sometimes wait&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we're left asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why didn’t my timeout fire?&lt;/li&gt;
&lt;li&gt;Why didn’t it interrupt my loop?&lt;/li&gt;
&lt;li&gt;Why does &lt;code&gt;await&lt;/code&gt; pause &lt;em&gt;this&lt;/em&gt; but not &lt;em&gt;everything&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;Why does nothing ever 'cut in'?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this series, we'll build the understanding needed to make JavaScript stop feeling magical. &lt;/p&gt;

&lt;p&gt;Today's core claim is simple: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;JavaScript executes synchronously inside a task, and nothing can interrupt that execution.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What Does "Synchronous" and "Asynchronous" Really Mean?
&lt;/h2&gt;

&lt;p&gt;First, let's define 'synchronous' and 'asynchronous' precisely.&lt;/p&gt;

&lt;p&gt;Synchronous execution refers to code that runs immediately, executing from top to bottom via the call stack. We will show that this cannot be interrupted in JavaScript. &lt;/p&gt;

&lt;p&gt;By contrast, asynchronous code refers to code whose result is not available immediately. Some work is initiated now, but its continuation runs later. In practice, this almost always involves a &lt;strong&gt;callback&lt;/strong&gt; - a function that is scheduled to execute in the future. In JavaScript, asynchronous code includes not only &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; but also other mechanisms like &lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;Promise&lt;/code&gt;, and &lt;code&gt;requestAnimationFrame&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;As this series will make clear, asynchronous mechanisms do not block nor interrupt the call stack. Instead, they arrange for something to run later via scheduling.&lt;/p&gt;

&lt;p&gt;For now, let's test the claim directly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can asynchronous callbacks interrupt synchronous execution?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Running the Experiments
&lt;/h2&gt;

&lt;p&gt;You can run all code snippets in this series by pasting them into the browser console.&lt;/p&gt;

&lt;p&gt;While some examples work in Node.js, others rely on browser APIs (like rendering or &lt;code&gt;requestAnimationFrame&lt;/code&gt;), so the browser is the most reliable environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Baseline Case: Pure Synchronous Execution
&lt;/h2&gt;

&lt;p&gt;Let's start with a simple &lt;code&gt;for&lt;/code&gt; loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code runs from top to bottom. The output is unsurprisingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sync start
sync end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Test 1: Can &lt;code&gt;setTimeout&lt;/code&gt; Interrupt a Loop?
&lt;/h2&gt;

&lt;p&gt;Let's introduce an asynchronous mechanism with &lt;code&gt;setTimeout&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Schedule asynchronous callback with setTimeout&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timeout fired&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;setTimeout&lt;/code&gt; could interrupt, we would see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sync start
timeout fired
sync end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what actually happens is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sync start
sync end
timeout fired
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The timer did not 'cut in': It waited until the loop finished running. &lt;/p&gt;




&lt;h2&gt;
  
  
  Test 2: Can Promises Interrupt?
&lt;/h2&gt;

&lt;p&gt;Now let's try a &lt;code&gt;Promise&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Schedule asynchronous callback with a Promise&lt;/span&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;promise callback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Promises could interrupt execution, we would see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sync start
promise callback
sync end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the observed behavior is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sync start
sync end
promise callback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even a resolved &lt;code&gt;Promise&lt;/code&gt; does not interrupt synchronous execution. Once JavaScript starts running, it runs to completion. &lt;/p&gt;




&lt;h2&gt;
  
  
  Destroying the Wrong Mental Models
&lt;/h2&gt;

&lt;p&gt;It's easy to imagine JavaScript like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;JavaScript is running, but timers or promises can interrupt it when they're ready&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, &lt;code&gt;setTimeout&lt;/code&gt; fires when ready and &lt;code&gt;Promise&lt;/code&gt; callbacks can cut in once resolved. &lt;/p&gt;

&lt;p&gt;This resembles signal handlers in C where a signal can interrupt a running program, jump to the handler function and then resume execution afterward. That is pre-emptive behavior where execution can be paused at arbitrary instruction boundaries and control temporarily transferred elsewhere. JavaScript does not behave this way.&lt;/p&gt;

&lt;p&gt;We may also hold the mental model that: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Timers or promises run JavaScript on another thread.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this model, JavaScript runs concurrently in multiple threads: The script is running on one thread; Timers run in a background thread; Promises resolve in another (or the same) background thread. If that were true, we would see interleaved console output.  &lt;/p&gt;

&lt;p&gt;However, in both tests, there is no interruption nor interleaved output between &lt;code&gt;sync start&lt;/code&gt; and &lt;code&gt;sync end&lt;/code&gt; in the console. These tests force us to accept that JavaScript execution is not pre-emptive. When JavaScript starts executing a task, it continues until the call stack is empty. Only then can anything else run JavaScript. Asynchronous work waits and queues.&lt;/p&gt;




&lt;h2&gt;
  
  
  But Where Does Asynchronous Work Wait?
&lt;/h2&gt;

&lt;p&gt;JavaScript's behavior makes more sense when you realize that when you run JavaScript in the browser, you are not just running a language. You are running two cooperating systems: the JavaScript &lt;strong&gt;Engine&lt;/strong&gt; and the JavaScript &lt;strong&gt;Runtime Environment&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The engine (V8 in Chrome/Node, SpiderMonkey in Firefox, JavaScriptCore in Safari) parses and compiles code, manages memory and executes functions via the call stack. When we say "JavaScript runs", we are referring to the engine. It knows variables, functions and the call stack. It does &lt;strong&gt;not&lt;/strong&gt; know timers, the DOM, HTTP requests nor rendering. &lt;/p&gt;

&lt;p&gt;Everything else is handled by the runtime (the browser or Node). For instance, the runtime provides web APIs (&lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;fetch&lt;/code&gt;, DOM events). Importantly, it also performs the asynchronous work &lt;em&gt;outside&lt;/em&gt; the engine and decides when JavaScript is allowed to run again. This is the missing link: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The engine executes. The runtime schedules. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's revisit our first experiment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timeout fired&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the loop is running, what is happening structurally?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────────┐  ┌───────────────────────────────┐
│         CALL STACK            │  │         TASK QUEUE            │
├───────────────────────────────┤  ├───────────────────────────────┤
│ for loop                      │  │ timeout callback (waiting)    │
│ global script                 │  |                               | 
└───────────────────────────────┘  └───────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;for&lt;/code&gt; loop occupies the call stack. Even after the timer has expired, its callback doesn't interrupt the call stack. Instead, it waits in a queue managed by the runtime. The engine cannot run it (nor anything new) because the call stack is not empty. More broadly, the runtime schedules what the engine executes while the engine just executes whatever gets placed on the stack. &lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing Tasks
&lt;/h2&gt;

&lt;p&gt;When the runtime grants permission for the engine to execute JavaScript, that moment is an entry point into execution. This entry point or permission to run JavaScript is called a &lt;strong&gt;task&lt;/strong&gt;. Examples include the initial script execution and the &lt;code&gt;setTimeout&lt;/code&gt; callback. Once a task starts, JavaScript runs synchronously. Asynchronous mechanisms do not interrupt a task but instead schedules future tasks. &lt;/p&gt;

&lt;p&gt;No worries if this doesn't make complete sense yet. We will refine "task" in later articles. For now, this is enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Sets Up
&lt;/h2&gt;

&lt;p&gt;From this article, we have established:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript runs synchronously inside a task&lt;/li&gt;
&lt;li&gt;Nothing can interrupt that execution&lt;/li&gt;
&lt;li&gt;Asynchronous mechanisms do not cut in - they schedule. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if asynchronous callbacks don't interrupt running code, how and when are they allowed to run? What exactly is a 'task'? Where are these queues? Who decides what runs next?&lt;/p&gt;

&lt;p&gt;This is the subject of the next article. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>JavaScript Event Loop Series: Building the Event Loop Mental Model from Experiments</title>
      <dc:creator>Marsha Teo</dc:creator>
      <pubDate>Fri, 10 Apr 2026 20:35:43 +0000</pubDate>
      <link>https://dev.to/marshateo/javascript-event-loop-series-building-the-event-loop-mental-model-from-experiments-4d8i</link>
      <guid>https://dev.to/marshateo/javascript-event-loop-series-building-the-event-loop-mental-model-from-experiments-4d8i</guid>
      <description>&lt;p&gt;I wrote this series because JavaScript has many "asynchronous" mechanisms (&lt;code&gt;await&lt;/code&gt;, &lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;Promise&lt;/code&gt;, &lt;code&gt;requestAnimationFrame&lt;/code&gt;) that look similar but behave very differently.&lt;/p&gt;

&lt;p&gt;At first, I assumed they were interchangeable but that assumption quickly broke when I started debugging: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why &lt;code&gt;setTimeout(..., 0)&lt;/code&gt; doesn’t "run immediately"&lt;/li&gt;
&lt;li&gt;Why &lt;code&gt;await&lt;/code&gt; pauses a function but doesn’t freeze the page&lt;/li&gt;
&lt;li&gt;Why DOM updates sometimes don’t show up when you expect&lt;/li&gt;
&lt;li&gt;Why some "async" code still blocks rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These behaviours only make sense with the right mental model. This series builds that model by experimenting with small code snippets. &lt;/p&gt;

&lt;p&gt;The core idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;JavaScript runs to completion inside a task, and nothing interrupts it. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From there, we layer in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macrotasks&lt;/strong&gt; (what a task actually is),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;microtasks&lt;/strong&gt; (why Promises run first), &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;&lt;/strong&gt; (pausing a function without pausing JavaScript), &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rendering&lt;/strong&gt; (why the screen doesn’t update mid-turn), &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;requestAnimationFrame&lt;/code&gt;&lt;/strong&gt; (the missing pre-render scheduling layer), and finally, &lt;/li&gt;
&lt;li&gt;what this means for &lt;strong&gt;real UI code&lt;/strong&gt;. &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;p&gt;This series is for you if you’ve ever felt that JavaScript async behavior is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;predictable in practice, but unclear in theory
&lt;/li&gt;
&lt;li&gt;"working" … until it suddenly doesn’t
&lt;/li&gt;
&lt;li&gt;full of rules you remember, but don’t fully understand
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t need prior knowledge of the event loop. The goal is to build a mental model you can use to reason about behavior — not just memorize it.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to read this series
&lt;/h2&gt;

&lt;p&gt;Each article builds on the previous one. You &lt;em&gt;can&lt;/em&gt; jump around, but the payoff is highest if you go in order.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you want the &lt;strong&gt;core mental model quickly&lt;/strong&gt;: read Articles 1–3
&lt;/li&gt;
&lt;li&gt;If you care about &lt;strong&gt;rendering and UI behavior&lt;/strong&gt;: Articles 5–7 connect the model to what you see on screen
&lt;/li&gt;
&lt;li&gt;If you just want answers: each article is self-contained, but the full model only emerges across the series&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The articles
&lt;/h2&gt;

&lt;p&gt;Here’s how the model unfolds:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Before the Event Loop: What Actually Runs JavaScript&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why doesn’t &lt;code&gt;setTimeout&lt;/code&gt; interrupt your code? This article breaks the illusion: JavaScript runs synchronously, and async APIs don’t interrupt. Instead, they schedule.&lt;/p&gt;

&lt;p&gt;Read it &lt;a href="https://dev.to/marshateo/the-javascript-runtime-fixing-the-mental-model-5f5b"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Macrotasks: What a Task Actually Is&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If nothing can interrupt JavaScript, when does anything else run? This article reframes tasks as entry points into execution, not chunks of work.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Coming soon&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) Microtasks: Why Promises Run First&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why do Promises always run before &lt;code&gt;setTimeout&lt;/code&gt;? This article reveals microtasks as mandatory continuations that must run before JavaScript moves on.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Coming soon&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4) &lt;code&gt;async&lt;/code&gt; / &lt;code&gt;await&lt;/code&gt;: Pausing Functions Without Pausing the World&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Does &lt;code&gt;await&lt;/code&gt; pause your program or just your function? This article shows how &lt;code&gt;await&lt;/code&gt; actually works.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Coming soon&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5) Rendering Is a Browser Decision, Not a JavaScript One&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You updated the DOM. So why didn’t the screen change? This article explains why rendering is not triggered by JavaScript, but gated by it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Coming soon&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6) &lt;code&gt;requestAnimationFrame&lt;/code&gt;: The Missing Scheduling Layer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If rendering only happens at certain moments, how do you run code at the right time? This article introduces &lt;code&gt;requestAnimationFrame&lt;/code&gt; as the missing scheduling layer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Coming soon&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7) What the Event Loop Means for Real UI Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why do UIs freeze, skip updates, or feel laggy? This article connects the event loop to real-world UI behavior and shows how to work with the browser, not against it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Coming soon&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The mental model
&lt;/h2&gt;

&lt;p&gt;This is the model everything in this series builds toward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The browser (runtime) starts a macrotask
&lt;/li&gt;
&lt;li&gt;JavaScript runs synchronously until the call stack is empty
&lt;/li&gt;
&lt;li&gt;The runtime drains all microtasks
&lt;/li&gt;
&lt;li&gt;The browser runs any &lt;code&gt;requestAnimationFrame&lt;/code&gt; callbacks
&lt;/li&gt;
&lt;li&gt;Microtasks drain again (if any were queued during &lt;code&gt;requestAnimationFrame&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Only then can rendering happen
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Continue the Conversation
&lt;/h2&gt;

&lt;p&gt;If you want to discuss edge cases, counterexamples, or how this interacts with real applications, I’m always happy to chat.&lt;/p&gt;

&lt;p&gt;You can find more of my writing at &lt;a href="https://marshateo.com" rel="noopener noreferrer"&gt;https://marshateo.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
