A JavaScript runtime is the environment that runs JavaScript and gives it tools to work with the outside world.
JavaScript can calculate a total by itself. But reading a file, updating a web page, or starting a timer needs help from its environment.
Language, engine, and runtime
These words describe different things:
| Term | Meaning | Example |
|---|---|---|
| JavaScript language | Rules for writing programs | Variables, functions, objects, promises |
| JavaScript engine | Software that executes JavaScript | V8, SpiderMonkey, JavaScriptCore |
| JavaScript runtime | The engine plus the surrounding environment | A browser or Node.js |
Chrome and Node.js both use V8, but they provide different tools. A shared engine does not mean a shared environment.
JavaScript runtime
|-- JavaScript engine
| |-- Call stack: tracks running function calls
| |-- Heap: memory used for objects and other data
|
|-- Host APIs: timers, network, files, or page tools
|-- Scheduling: event loop, tasks, and microtasksThis is a learning diagram, not an exact map of every implementation.
Browser runtime vs. Node.js runtime
| Feature | Browser | Node.js |
|---|---|---|
| Typical use | Interactive web pages | Servers, scripts, command-line tools |
| Page tools | window, document, DOM |
No browser DOM by default |
| File access | Restricted browser APIs | Modules such as node:fs |
| Timers | setTimeout, setInterval |
setTimeout, setInterval |
| Global environment | globalThis |
globalThis |
The DOM is the browser's object representation of an HTML page. JavaScript uses it to read and change the page.
document, setTimeout, and console are supplied by the environment. They are not JavaScript language syntax. Promise, Array, and Object belong to the language's standard built-in tools.
The runnable examples mentioned below (
01-…js,02-…jsand so on) live in the source folder on GitHub. Each runs directly withnode <file>— no packages needed.
Call stack: which function is running?
The call stack tracks active function calls. A function call adds a frame. Returning from the function removes that frame. The last frame added is the first one removed.
In 02-call-stack.js, greet() calls makeMessage():
greet starts
makeMessage starts
makeMessage returns
greet continues
greet returnsExpected output:
1. Script starts
2. Inside greet
3. Inside makeMessage
4. Hello, Asha!
5. Script endsEach call finishes its synchronous work before the caller continues. Too many nested calls, such as recursion with no stopping condition, can overflow the stack.
Heap: objects and references
The heap is a memory area used for objects and other dynamically managed data. Engines decide the exact storage details; do not assume every primitive always lives on the stack.
const first = { name: "Asha" };
const second = first; // Both variables refer to the same object.
second.name = "Mina";
console.log(first.name); // MinaAssignment does not automatically copy an object. 03-heap-and-references.js also demonstrates { ...object }. This creates a shallow copy: the outer object is new, but nested objects are still shared.
JavaScript uses garbage collection to reclaim memory that is no longer reachable. You do not normally free objects manually. Keeping unnecessary references, such as old objects in a global array, can keep memory in use. Collection timing is controlled by the engine.
Synchronous vs. asynchronous work
Synchronous work runs now, in order. The next statement waits for it to finish.
Asynchronous operations allow a result to arrive later. The runtime can manage a timer or I/O operation while your JavaScript continues. A callback or promise continuation handles the result later.
console.log("Start");
setTimeout(() => console.log("Timer finished"), 0);
console.log("End");Output: Start, End, then Timer finished.
setTimeout(..., 0) does not mean “run immediately.” It schedules a callback for a later opportunity. The current synchronous code must finish first. Timer delays are not exact appointment times.
Event loop, tasks, and microtasks
The event loop coordinates when queued work can run.
For the simple examples here:
- Run the current synchronous code.
- At the microtask checkpoint, process pending microtasks, including newly added microtasks.
- Run eligible later work, such as a timer callback, when the runtime schedules it.
Promise .then() handlers and queueMicrotask() callbacks use microtasks. Timer callbacks are later tasks in the browser model. Node.js has its own event-loop phases, so a single queue picture does not explain all Node behavior.
Expected output of 04-event-loop.js:
1. Synchronous start
2. Promise executor runs immediately
3. Synchronous end
4. Promise handler (microtask)
5. queueMicrotask callback
6. Timer callbackThe function passed to new Promise(...) runs immediately. Its .then() handler runs later as a microtask. Microtasks in this example run in the order they were queued.
Repeatedly adding microtasks can delay other work. Also, Node's process.nextTick() and setImmediate() have additional scheduling rules; do not apply this beginner example as a universal ordering rule for every API.
What async and await really do
An async function always returns a promise. It runs synchronously until it reaches an await that suspends it. await pauses that function's continuation while other code can run. It does not stop the whole runtime.
In 05-async-await.js, a timer simulates a delayed result. There is no real network request.
Expected output:
1. Before calling showUser
2. Loading user...
3. Other code can run
4. User: Asha
5. Loading finishedUse try/catch around awaited work to handle rejected promises. Making a function async does not move heavy calculations to another thread.
Single thread does not mean nothing else happens
Within one JavaScript execution thread, callbacks execute one at a time. However, the runtime can use the operating system and additional threads to support work such as I/O. Browsers and Node.js also support workers for running JavaScript on other threads.
A long loop still blocks the current JavaScript thread. During that loop, timer callbacks cannot execute on the same thread. In a browser, heavy work on the main thread can also delay page interaction and rendering.
06-blocking-code.js deliberately blocks for about 150 milliseconds. Its timer has a zero delay, but its callback runs only after the loop finishes. The measured time varies by machine. This short busy loop is a demonstration, not a recommended waiting technique.
For real CPU-heavy work, consider workers. For large tasks on the main thread, consider splitting work into smaller pieces and yielding between them.
Node.js provides file-system tools
07-node-file-system.js reads this guide with node:fs/promises. The path uses __dirname, so it works even when you start the script from another directory.
Expected output:
1. Starting file read
2. JavaScript continues while the file is being read
3. First line: # JavaScript Runtime — A Beginner-Friendly GuideThe read is asynchronous. Node supplies file access; the language alone does not. The example handles errors and sets a nonzero exit code if reading fails.
Browser runtime example
Open 08-browser-runtime.html, then click Run example. The browser provides:
documentto find and update elements.addEventListener()to handle clicks.setTimeout()to schedule later work.
The page shows synchronous messages first, then the promise microtask, then the timer. Open the browser's developer tools Console to see the same messages. Each click starts a fresh demonstration.
Common mistakes
| Mistake | Better understanding |
|---|---|
| JavaScript engine and runtime mean the same thing | The runtime includes an engine and supporting facilities |
| A zero-delay timer runs immediately | It runs later when scheduling allows |
| A promise executor runs asynchronously | The executor runs synchronously; handlers run later |
await blocks the entire program |
It suspends the current async function's continuation |
async makes CPU-heavy code nonblocking |
Synchronous calculations still occupy the thread |
| Copying an object variable copies its data | It copies the reference |
| Browser JavaScript and Node have identical globals | Their host APIs differ |
Practice
- Before running
04-event-loop.js, predict the output. Then movequeueMicrotask()above the promise creation and predict it again. - Add a second timer to that file. Keep both delays at zero and observe the result in your environment.
- In
03-heap-and-references.js, make a separate copy of the nestedaddressobject. Check whether changing the copy still changes the original. - In
05-async-await.js, reject the simulated request withreject(new Error("User not found"))and observe the error handler. - Add another button to the HTML page that changes the page's background color through the DOM.
Read the files in number order. Predict the output first, run the example, and explain why each line appears when it does.
The runnable examples for this note are on GitHub.
View source