The Single Threaded Reality
JavaScript is a single-threaded, synchronous language. To understand asynchronicity, we first have to master its synchronous foundations.
After mastering the metaprogramming funnels of coercion, we're ready to tackle the architectural feature that makes the modern web possible: Asynchronous JavaScript.
Promises, the event loop, and microtask queues are often seen as "magic" to a fault. But they are built on a very strict, very predictable foundation. To understand how they work, we must first remember how JavaScript runs code by default.
The Essentials
- Single Thread: JavaScript can only do one thing at a time. It executes code line-by-line.
- Synchronous Execution: Every line of code must finish before the next one starts.
- The Engine: Comprised of the Thread of Execution, Global Memory, and the Call Stack.
The Core Model: A Quick Review
When JavaScript starts running, it creates a Global Execution Context. This consists of two main parts: the thread of execution and memory.
Let's trace a simple, synchronous snippet:
const num = 3;
function multiplyBy2(input) {
const result = input * 2;
return result;
}
const output = multiplyBy2(num);
const newOutput = multiplyBy2(10);As we move through this code, our Thread of Execution goes line-by-line:
- Line 1: Declare
numin global memory and assign it3. - Line 2: Declare function
multiplyBy2and store its entire definition. - Line 7: Declare
output. Before we can assign it a value, we must runmultiplyBy2(3).- A new Execution Context is created and pushed onto the Call Stack.
- Inside, the parameter
inputis assigned3. resultbecomes6.- The
returnkeyword sends6back to global memory. - The context is popped off the stack and deleted.
- Line 8: Declare
newOutput. Again, we must run the function and finish it before we can move on.
Step 1:Global memory stores num as 3.
The "Blocking" Problem
This model is super predictable. But it has a fatal flaw: it is blocking.
Imagine if instead of multiplyBy2, we had a function getVideosFromTikTok. If that function had to speak to a server in West Virginia to get links, it might take 300 milliseconds. In the world of high-speed computing, 300ms is an eternity.
Because JavaScript is single-threaded, if we ran that function synchronously, no other code could run while we waited. The user interface would freeze. Buttons wouldn't click. Animations would stop.
The Solution: JavaScript is Not Alone
JavaScript does not run in isolation. It runs inside a Web Browser (or a Node environment). The browser is not single-threaded. It has a whole suite of powerful features that are not part of JavaScript itself:
- The DOM: The model of the webpage.
- Network Requests: Speaking to the internet (fetch).
- Timers: Keeping track of time (setTimeout).
- Console: Logging to dev tools.
Facade Functions
To access these features, we use Facade Functions. They look like regular JavaScript functions (like setTimeout or fetch), but they are actually portals to the browser's background features.
In the next part, we'll see how we can "spin up" these browser features to do work in the background without blocking our single thread.
Further Reading and Watching
- Video: JavaScript: The Hard Parts (Async Intro)
- Concept: The Single Threaded Nature of JS
- Reference: Web APIs on MDN
Keep reading