Storage, Network and Platform APIs

The language is JavaScript; the host adds the rest. Browsers hand you network, storage, URL, and hardware APIs as globals — no install, no import. This lesson covers the ones every vanilla project touches: timers, storage, and 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.ok on every fetch, because 404 does not throw.
  • You can cancel a fetch with AbortController and handle the AbortError branch.