JavaScript
1 min read
Updated 4 Aug 2026
Part 2 — Loading & Running
Script loading: defer vs async vs module 🎯, runtimes, the console snapshot trap
Script loading — know this table 🎯#
<script src="app.js"></script> <!-- blocks HTML parsing -->
<script defer src="app.js"></script> <!-- parallel download, run after parse, in order -->
<script async src="app.js"></script> <!-- parallel download, run ASAP, no order -->
<script type="module" src="app.js"></script> <!-- deferred by default, strict, own scope -->
| Attribute | Downloads | Executes | Order kept? |
|---|---|---|---|
| (none) | immediately, blocks parsing | immediately | yes |
defer |
in parallel | after HTML parsed, before DOMContentLoaded |
yes |
async |
in parallel | as soon as downloaded (may interrupt parse) | no |
type="module" |
deferred | after parse | yes |
🟢 Prefer defer or modules — the script sees a fully-built DOM without blocking render. async suits independent scripts (analytics) where order doesn't matter.
Runtimes#
Same engine, different host APIs:
- Browser — DOM,
fetch, timers, Web APIs. - Node —
fs,http, streams,process. Switch to ESM with"type": "module"inpackage.json(or.mjs). - Deno / Bun — modern runtimes: TypeScript out of the box, fast, batteries included.
The console snapshot trap ⚠️#
console.log(obj); // some browsers render obj as it is when you EXPAND it,
// not when you logged it → confusing during mutation
console.log(structuredClone(obj)); // 🟢 snapshot the value at log time