{
  "source_file": "0930",
  "source_platform": "Dev.to",
  "author": "Marsha Teo",
  "title_listed": "The JavaScript Runtime: Fixing the Mental Model",
  "keywords": "",
  "domain": "机器学习",
  "content_type": "深度技术解析",
  "is_paid_flag": "否",
  "url": "https://dev.to/marshateo/the-javascript-runtime-fixing-the-mental-model-5f5b",
  "publish_date": "2026-04-10",
  "corpus_stage": "",
  "notes": "",
  "id": "87121c14f58b",
  "collected_at": "2026-10-02T14:06:28",
  "title": "The JavaScript Runtime: Fixing the Mental Model - DEV Community",
  "body_markdown": "This is the first article in a series on how JavaScript actually runs. You can read the full series\nhere\nor on my\nwebsite\n.\nMost explanations of JavaScript's event loop start with:\nJavaScript is single-threaded.\nThis statement is technically true but doesn't explain certain behaviors in JavaScript you may have noticed:\nsetTimeout\ndoesn't interrupt loops after the timer has run out\nsetTimeout\ndoesn't block (like\nsleep(1)\nin C)\nA resolved\nPromise\nstill runs after synchronous code\nawait\npauses a function but doesn't freeze the page\nRendering sometimes wait\nSo we're left asking:\nWhy didn’t my timeout fire?\nWhy didn’t it interrupt my loop?\nWhy does\nawait\npause\nthis\nbut not\neverything\n?\nWhy does nothing ever 'cut in'?\nIn this series, we'll build the understanding needed to make JavaScript stop feeling magical.\nToday's core claim is simple:\nJavaScript executes synchronously inside a task, and nothing can interrupt that execution.\nWhat Does \"Synchronous\" and \"Asynchronous\" Really Mean?\nFirst, let's define 'synchronous' and 'asynchronous' precisely.\nSynchronous 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.\nBy 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\ncallback\n- a function that is scheduled to execute in the future. In JavaScript, asynchronous code includes not only\nasync\n/\nawait\nbut also other mechanisms like\nsetTimeout\n,\nPromise\n, and\nrequestAnimationFrame\n.\nAs 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.\nFor now, let's test the claim directly:\nCan asynchronous callbacks interrupt synchronous execution?\nRunning the Experiments\nYou can run all code snippets in this series by pasting them into the browser console.\nWhile some examples work in Node.js, others rely on browser APIs (like rendering or\nrequestAnimationFrame\n), so the browser is the most reliable environment.\nBaseline Case: Pure Synchronous Execution\nLet's start with a simple\nfor\nloop:\nconsole\n.\nlog\n(\n\"\nsync start\n\"\n);\nfor\n(\nlet\ni\n=\n0\n;\ni\n<\n1\ne9\n;\ni\n++\n)\n{}\nconsole\n.\nlog\n(\n\"\nsync end\n\"\n);\nEnter fullscreen mode\nExit fullscreen mode\nThe code runs from top to bottom. The output is unsurprisingly:\nsync start\nsync end\nEnter fullscreen mode\nExit fullscreen mode\nTest 1: Can\nsetTimeout\nInterrupt a Loop?\nLet's introduce an asynchronous mechanism with\nsetTimeout\n:\nconsole\n.\nlog\n(\n\"\nsync start\n\"\n);\n// Schedule asynchronous callback with setTimeout\nsetTimeout\n(()\n=>\n{\nconsole\n.\nlog\n(\n\"\ntimeout fired\n\"\n);\n},\n0\n);\nfor\n(\nlet\ni\n=\n0\n;\ni\n<\n1\ne9\n;\ni\n++\n)\n{}\nconsole\n.\nlog\n(\n\"\nsync end\n\"\n);\nEnter fullscreen mode\nExit fullscreen mode\nIf\nsetTimeout\ncould interrupt, we would see:\nsync start\ntimeout fired\nsync end\nEnter fullscreen mode\nExit fullscreen mode\nBut what actually happens is this:\nsync start\nsync end\ntimeout fired\nEnter fullscreen mode\nExit fullscreen mode\nThe timer did not 'cut in': It waited until the loop finished running.\nTest 2: Can Promises Interrupt?\nNow let's try a\nPromise\n:\nconsole\n.\nlog\n(\n\"\nsync start\n\"\n);\n// Schedule asynchronous callback with a Promise\nPromise\n.\nresolve\n().\nthen\n(()\n=>\n{\nconsole\n.\nlog\n(\n\"\npromise callback\n\"\n);\n});\nfor\n(\nlet\ni\n=\n0\n;\ni\n<\n1\ne9\n;\ni\n++\n)\n{}\nconsole\n.\nlog\n(\n\"\nsync end\n\"\n);\nEnter fullscreen mode\nExit fullscreen mode\nIf Promises could interrupt execution, we would see:\nsync start\npromise callback\nsync end\nEnter fullscreen mode\nExit fullscreen mode\nBut the observed behavior is:\nsync start\nsync end\npromise callback\nEnter fullscreen mode\nExit fullscreen mode\nEven a resolved\nPromise\ndoes not interrupt synchronous execution. Once JavaScript starts running, it runs to completion.\nDestroying the Wrong Mental Models\nIt's easy to imagine JavaScript like this:\nJavaScript is running, but timers or promises can interrupt it when they're ready\nIn other words,\nsetTimeout\nfires when ready and\nPromise\ncallbacks can cut in once resolved.\nThis 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.\nWe may also hold the mental model that:\nTimers or promises run JavaScript on another thread.\nIn 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.\nHowever, in both tests, there is no interruption nor interleaved output between\nsync start\nand\nsync end\nin 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.\nBut Where Does Asynchronous Work Wait?\nJavaScript'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\nEngine\nand the JavaScript\nRuntime Environment\n.\nThe 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\nnot\nknow timers, the DOM, HTTP requests nor rendering.\nEverything else is handled by the runtime (the browser or Node). For instance, the runtime provides web APIs (\nsetTimeout\n,\nfetch\n, DOM events). Importantly, it also performs the asynchronous work\noutside\nthe engine and decides when JavaScript is allowed to run again. This is the missing link:\nThe engine executes. The runtime schedules.\nLet's revisit our first experiment:\nconsole\n.\nlog\n(\n\"\nsync start\n\"\n);\nsetTimeout\n(()\n=>\n{\nconsole\n.\nlog\n(\n\"\ntimeout fired\n\"\n);\n},\n0\n);\nfor\n(\nlet\ni\n=\n0\n;\ni\n<\n1\ne9\n;\ni\n++\n)\n{}\nconsole\n.\nlog\n(\n\"\nsync end\n\"\n);\nEnter fullscreen mode\nExit fullscreen mode\nWhile the loop is running, what is happening structurally?\n┌───────────────────────────────┐  ┌───────────────────────────────┐\n│         CALL STACK            │  │         TASK QUEUE            │\n├───────────────────────────────┤  ├───────────────────────────────┤\n│ for loop                      │  │ timeout callback (waiting)    │\n│ global script                 │  |                               | \n└───────────────────────────────┘  └───────────────────────────────┘\nEnter fullscreen mode\nExit fullscreen mode\nThe\nfor\nloop 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.\nIntroducing Tasks\nWhen 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\ntask\n. Examples include the initial script execution and the\nsetTimeout\ncallback. Once a task starts, JavaScript runs synchronously. Asynchronous mechanisms do not interrupt a task but instead schedules future tasks.\nNo worries if this doesn't make complete sense yet. We will refine \"task\" in later articles. For now, this is enough.\nWhat This Sets Up\nFrom this article, we have established:\nJavaScript runs synchronously inside a task\nNothing can interrupt that execution\nAsynchronous mechanisms do not cut in - they schedule.\nBut 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?\nThis is the subject of the\nnext article\n.\nThis article was originally published on my\nwebsite\n.",
  "metadata": {
    "title": "The JavaScript Runtime: Fixing the Mental Model - DEV Community",
    "description": "This is the first article in a series on how JavaScript actually runs. You can read the full series... Tagged with javascript, webdev.",
    "keywords": "javascript, webdev, software, coding, development, engineering, inclusive, community",
    "author": null,
    "og:type": "article",
    "og:url": "https://dev.to/marshateo/the-javascript-runtime-fixing-the-mental-model-5f5b",
    "og:title": "The JavaScript Runtime: Fixing the Mental Model",
    "og:description": "This is the first article in a series on how JavaScript actually runs. You can read the full series...",
    "og:site_name": "DEV Community",
    "og:image": "https://media2.dev.to/dynamic/image/width=1200,height=627,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwtql1dtliywrq5cyns9q.png",
    "twitter:site": "@thepracticaldev",
    "twitter:creator": "@",
    "twitter:title": "The JavaScript Runtime: Fixing the Mental Model",
    "twitter:description": "This is the first article in a series on how JavaScript actually runs. You can read the full series...",
    "twitter:card": "summary_large_image",
    "twitter:widgets:new-embed-design": "on",
    "twitter:image:src": "https://media2.dev.to/dynamic/image/width=1200,height=627,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwtql1dtliywrq5cyns9q.png"
  },
  "paywall": {
    "listed_paid": false,
    "detected": false,
    "marker": "",
    "public_only": false,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 200,
    "error": ""
  },
  "code_blocks": [
    {
      "file": "attachments/87121c14f58b/code/body-000.txt",
      "chars": 87,
      "language": "text"
    },
    {
      "file": "attachments/87121c14f58b/code/body-001.txt",
      "chars": 20,
      "language": "text"
    },
    {
      "file": "attachments/87121c14f58b/code/body-002.txt",
      "chars": 196,
      "language": "text"
    },
    {
      "file": "attachments/87121c14f58b/code/body-003.txt",
      "chars": 34,
      "language": "text"
    },
    {
      "file": "attachments/87121c14f58b/code/body-004.txt",
      "chars": 34,
      "language": "text"
    },
    {
      "file": "attachments/87121c14f58b/code/body-005.txt",
      "chars": 207,
      "language": "text"
    },
    {
      "file": "attachments/87121c14f58b/code/body-006.txt",
      "chars": 37,
      "language": "text"
    },
    {
      "file": "attachments/87121c14f58b/code/body-007.txt",
      "chars": 37,
      "language": "text"
    },
    {
      "file": "attachments/87121c14f58b/code/body-008.txt",
      "chars": 146,
      "language": "text"
    },
    {
      "file": "attachments/87121c14f58b/code/body-009.txt",
      "chars": 415,
      "language": "text"
    }
  ],
  "images": [],
  "audio": [],
  "videos": [],
  "links": [
    {
      "href": "https://dev.to/marshateo/javascript-event-loop-series-building-the-event-loop-mental-model-from-experiments-4d8i",
      "label": "here",
      "type": "a"
    },
    {
      "href": "https://www.marshateo.com/writing/javascript-event-loop-landing",
      "label": "website",
      "type": "a"
    },
    {
      "href": "https://dev.to/marshateo/macrotasks-what-a-task-actually-is-4pbd",
      "label": "next article",
      "type": "a"
    },
    {
      "href": "https://www.marshateo.com/writing/runtime-mental-model",
      "label": "website",
      "type": "a"
    }
  ],
  "repair_version": 2,
  "repaired_at": "2026-10-14T10:50:23Z",
  "source_url": "https://dev.to/marshateo/the-javascript-runtime-fixing-the-mental-model-5f5b",
  "papers": [],
  "actual_video_files": [],
  "body_chars": 8286,
  "expected_body_images": 0,
  "discovery_status": "ok",
  "delivery_status": "ok",
  "missing_reason": "",
  "raw_html": "attachments/87121c14f58b/pages/source.html",
  "local_html": "attachments/87121c14f58b/pages/article.html",
  "body_base_path": "attachments/87121c14f58b/pages/",
  "body_markdown_path": "attachments/87121c14f58b/pages/article.md"
}