Storage, Network and Platform APIs
fetch.
The language versus the platform
setTimeout is not part of JavaScript — nor is document,
localStorage, or fetch. They live on the host's global object
(window in a browser, globalThis everywhere, process
on Node). That is why a browser API throws ReferenceError in Node and vice
versa. Everything below is "same language, bigger global".
Timers — and the minimum-delay trap
The delay is a minimum, not a target
// Browser context.
console.log("start");
setTimeout(() => console.log("done"), 0); // "0 ms" means: as soon as the queue allows
console.log("end");
// Expected output:
// start
// end
// done
Timers never run early — and may run late, because the main thread must finish its current
work first (the event loop, Phase 4, has the full story). A 0 delay still runs
only after the current script completes.
const id = setInterval(tick, 1000); // returns a handle…
clearInterval(id); // …without clearing, it runs FOREVER —
// the classic forgotten-timer leak (lesson 15)
Web storage — strings only
The type you did not save is not there
// Browser context.
localStorage.setItem("score", 99); // the number 99 is coerced…
const raw = localStorage.getItem("score"); // …into the STRING "99"
console.log(typeof raw); // string — always, for any value
// Round-trip objects with JSON; storage has no object type of its own:
localStorage.setItem("user", JSON.stringify({ id: 7, role: "admin" }));
const user = JSON.parse(localStorage.getItem("user"));
console.log(user.role); // admin
Tiers, briefly: localStorage persists until cleared (~5 MB, same-origin),
sessionStorage clears with the tab, cookies ride every request (use only for
server-session data), and IndexedDB is the asynchronous database for large structured data.
fetch — network requests as promises
fetch does NOT reject on 404 — the classic trap
// Browser context. A 404 is a valid HTTP response — the network WORKED.
async function loadUser(id) {
const response = await fetch(`/api/users/${id}`);
// fetch rejects only on network failure (offline, DNS, CORS block…).
if (!response.ok) { // ok === status in 200–299
throw new Error(`HTTP ${response.status} for user ${id}`); // make 404 loud ourselves
}
return response.json(); // ALSO a promise — await it too
}
loadUser(999).catch((error) => console.log(error.message));
// Expected output (when the route 404s):
// HTTP 404 for user 999
async/await gets its full lesson in Phase 4 — read it here as
"pause until the response arrives".
Cancelling a slow request with AbortController
const controller = new AbortController();
fetch("/api/search", { signal: controller.signal })
.then((r) => r.json())
.catch((error) => {
if (error.name === "AbortError") {
console.log("user navigated away — expected, not an error");
} else {
throw error; // real failures still surface
}
});
controller.abort(); // the caller decides when waiting is no longer worth it
URL and History — reading the address bar
// Browser context, on https://sagecode.org/roadmap/javascript/index.html?lang=js&page=2
const url = new URL(location.href);
console.log(url.pathname); // /roadmap/javascript/index.html
console.log(url.searchParams.get("lang")); // js — parsed, decoded, no regex needed
history.pushState({}, "", "?lang=js&page=3"); // changes the bar WITHOUT reloading —
// the basis of every client-side router
Practice: fetch, cancel, cache
The task
Open demo runtime/25_storage_lab.html (Preview link on the Lab Examples page).
Save a score raw and as JSON; read it back and predict each typeof; reload the
page and confirm persistence. Then write loadWithCache(url): check
localStorage first, fetch only on miss, and store the JSON result
before returning it.
The checklist
- You can say which side provides setTimeout, document, and fetch — and why Node lacks one.
- You know storage saves strings only, and round-trip objects through JSON.
- You check
response.okon every fetch, because 404 does not throw. - You can cancel a fetch with AbortController and handle the AbortError branch.