How JavaScript Works

This section gives you a big picture of how JavaScript runs your code. Some of this, especially the parts about single-threaded execution and the event loop, might not fully make sense yet. That is fine. We will come back to these ideas when we cover asynchronous programming later in the course.

JavaScript Engines

A JavaScript engine is the program that executes JavaScript code. Different browsers and environments use different engines:

Engine Used By
V8 Chrome, Edge, Node.js, Deno
SpiderMonkey Firefox
JavaScriptCore (Nitro) Safari, Bun

Modern JavaScript engines use Just-In-Time (JIT) compilation. Instead of interpreting code line by line (slow) or compiling everything up front (delay before execution), JIT compilation:

  1. Starts executing code immediately (like an interpreter)
  2. Identifies “hot” code paths that run frequently
  3. Compiles those paths to optimized machine code
  4. Continues optimizing as the program runs

For many tasks, this gets JavaScript close to the speed of a compiled language.

Runtime Environments

A JavaScript engine alone only understands the core language (variables, functions, objects, etc.). A runtime environment adds APIs that let JavaScript interact with the outside world.

Browser Runtime

In a browser, the runtime provides:

  • DOM API for manipulating web pages
  • Fetch API for making HTTP requests
  • Web Storage for storing data locally
  • Canvas/WebGL for graphics
  • Web Audio for sound
  • And many more Web APIs

Server-Side Runtimes

Outside the browser, runtimes like Node.js provide:

  • File system access
  • Network capabilities (HTTP servers, TCP/UDP)
  • Process management
  • OS information

The core JavaScript language is the same everywhere, but the available APIs differ based on the runtime.

Single-Threaded Execution

JavaScript executes code on a single thread—only one piece of code runs at a time. So what happens when one piece of code takes a long time?

Imagine you click a button on a web page, and the button’s code needs to fetch data from a server. If JavaScript waited for the server to respond before doing anything else, the entire page would freeze. You could not scroll, click other buttons, or even select text. The browser would become unresponsive until the data arrived.

This is called blocking, and it makes the page unresponsive. JavaScript solves this problem with an event-driven, non-blocking model.

The Event Loop

Instead of waiting for slow operations to complete, JavaScript:

  1. Starts the operation (like a network request) and registers what should happen when it finishes
  2. Continues executing other code immediately
  3. Handles the result later when the operation completes and JavaScript is free

The part that coordinates this is called the event loop. It keeps checking whether anything is waiting to run. When your network request finishes, the event loop schedules your code to run once JavaScript is done with whatever it is doing.

This is why a web page stays responsive while it is loading data. It is also why Node.js can handle thousands of connections at once. It does not wait for any single operation to finish before handling the others.

We will cover the event loop in detail in the chapter on Asynchronous Programming. That is where you will learn the patterns (callbacks, promises, async/await) that make this model practical to work with.

Implications of the Execution Model

Understanding JavaScript’s execution model helps explain:

  • Why asynchronous programming is central to JavaScript
  • Why heavy computation can freeze a web page (it blocks the single thread)
  • Why Node.js is good at I/O-heavy workloads
  • Why patterns like callbacks and promises exist