Checkpoint: Browser APIs

Reviewed & published by Brayan K

This checkpoint consolidates the async and browser-API skills you have built — promise combinators, async iterators, cancellation, workers, observers, storage, and canvas — into one recap, a real build challenge, and a quick quiz.

Part of the free JavaScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

If you can finish the challenge and answer the quiz, you are ready to ship features that fetch, stream, cache, and render real data.

What You'll Do in This Checkpoint

What You've Learned

Here is the whole stretch in one place. Skim it; anything that feels fuzzy is worth a quick revisit.

TopicThe one thing to remember
Promise combinatorsall is fail-fast, allSettled keeps every result, race takes the first, any the first success.
Async iteratorsasync function* + for await stream values lazily, one await at a time.
AbortControllerA one-shot signal cancels work; fetch rejects with an AbortError.
Web WorkersHeavy CPU work on another thread; talk only via postMessage; data is cloned.
IntersectionObserverEfficiently detect visibility for lazy loading and infinite scroll — no scroll polling.
MutationObserverReact to DOM changes (nodes, attributes, text) as batched records.
IndexedDBA real client-side database: object stores, transactions, indexes — usually promise-wrapped.
Canvas APIDraw on the 2D context; top-left origin, radians for arcs, repaint each frame.

🛠️ Build Challenge — A Resilient Data Pipeline

Tie three skills together into one pipeline that real apps actually need:

The starter below already runs end to end against a fake paged API (page 2 fails on purpose). Read it, run it, then try extending it — for example add a real cachedFetch that consults the cache before hitting the network, then compare with the full solution.

// A fake paged API (stands in for fetch). Page 2 fails on purpose.
const DB = new Map([
  [1, { items: ["alpha", "beta"], next: 2 }],
  [2, { items: ["gamma"], next: 3 }],
  [3, { items: ["delta", "epsilon"], next: null }],
]);
const fetchPage = (page) =>
  new Promise((resolve, reject) =>
    setTimeout(() => {
      if (page === 2) return reject(new Error("page 2 down"));
      resolve(DB.get(page));
    }, 5)
  );

const cache = new Map();

// STEP 1 — fan out with Promise.allSettled and count failures
async function fetchBatch(pages) {
  const settled = await Promise.allSettled(pages.map(fetchPage));
  const ok = [];
  let failures = 0;
  for (let i = 0; i < settled.length; i++) {
    const r = settled[i];
    if (r.status === "fulfilled") {
      cache.set(pages[i], r.value); // STEP 3 — cache the good ones
      ok.push(r.value);
    } else {
      failures++;
    }
  }
  return { ok, failures };
}

// STEP 2 — async generator: stream items from the good pages
async function* streamItems(pages) {
  const { ok, failures } = await fetchBatch(pages);
  console.log("failures:", failures);
  for (const data of ok) {
    for (const item of data.items) yield item;
  }
}

async function main() {
  const out = [];
  for await (const item of streamItems([1, 2, 3])) out.push(item);
  console.log("items:", out);
  console.log("cached pages:", [...cache.keys()].sort());
}

main();
// ✅ Expected output:
// failures: 1
// items: [ 'alpha', 'beta', 'delta', 'epsilon' ]
// cached pages: [ 1, 3 ]

📝 Checkpoint Quiz

Answer each one in your head first, then expand to check. Aim to explain the why.

Promise.allSettled. It never rejects and returns a result object per promise, so you keep the successes and can count the failures.

for await (const x of gen()), and it must live inside an async function. A plain for-of throws on an async iterable.

Its promise rejects with a DOMException named "AbortError". Check err.name so you can ignore deliberate cancellations.

Messages are copied with the structured clone algorithm, which cannot clone functions or DOM nodes — it throws a DataCloneError. Send plain data only.

rootMargin (e.g. "200px 0px") on an IntersectionObserver. It expands the viewport box so elements count as visible early.

Stores are created only inside onupgradeneeded. The API uses request/event objects (onsuccess/onerror), so people wrap each request in a Promise to use async/await.

Quick Reference — One Line Each

APISignature
Keep all resultsawait Promise.allSettled(promises)
Stream valuesfor await (const x of gen())
Cancelfetch(url, { signal: c.signal })
Background workworker.postMessage(data)
Visibilityobserver.observe(el)
Store datadb.transaction("s", "readwrite")
Drawctx.fillRect(x, y, w, h)

📚 Keep Learning

🎉 Checkpoint Complete — Browser APIs!

Practice quiz

You fetch five widgets and want to show whichever ones load, even if some fail. Which combinator?

  • Promise.all
  • Promise.race
  • Promise.allSettled
  • Promise.any

Answer: Promise.allSettled. allSettled never rejects and returns a result object per promise, so you keep successes and count failures.

How does Promise.all behave when one of its promises rejects?

  • It rejects immediately (fail-fast)
  • It ignores the rejection
  • It returns the partial results
  • It retries the failed one

Answer: It rejects immediately (fail-fast). Promise.all is fail-fast: the first rejection rejects the whole thing.

What does Promise.race resolve to?

  • The first successful result only
  • All results in order
  • The slowest result
  • The result of whichever promise settles first

Answer: The result of whichever promise settles first. race settles with the first promise to settle, whether it fulfils or rejects.

What does Promise.any resolve to?

  • The first promise to settle
  • The first promise to fulfil (ignoring rejections)
  • All fulfilled values
  • The last rejection

Answer: The first promise to fulfil (ignoring rejections). any resolves with the first fulfilment and only rejects if every promise rejects.

What loop consumes an async function*, and what must surround it?

  • for await...of, inside an async function
  • for-of, inside any function
  • while, inside a generator
  • forEach, with await inside

Answer: for await...of, inside an async function. for await...of consumes async iterables and must live inside an async function.

After controller.abort(), what happens to an in-flight fetch using that signal?

  • It resolves with empty data
  • It silently completes
  • Its promise rejects with an AbortError
  • It retries automatically

Answer: Its promise rejects with an AbortError. The fetch rejects with a DOMException named AbortError; check err.name to ignore deliberate cancellations.

Why can't you pass a function to worker.postMessage()?

  • Functions are too large
  • Messages are structured-cloned, which can't clone functions
  • Workers only accept strings
  • It would create a security hole

Answer: Messages are structured-cloned, which can't clone functions. The structured clone algorithm cannot copy functions or DOM nodes — it throws a DataCloneError.

Which option lets IntersectionObserver lazy-load images shortly BEFORE they scroll into view?

  • threshold
  • delay
  • once
  • rootMargin

Answer: rootMargin. A positive rootMargin expands the viewport box so elements count as visible early.

Where must IndexedDB object stores be created?

  • In onsuccess
  • Inside the onupgradeneeded handler
  • Anywhere after open()
  • In a transaction's oncomplete

Answer: Inside the onupgradeneeded handler. Object stores and indexes can only be created during the onupgradeneeded event.

On the Canvas 2D context, where is the coordinate origin and what unit do arcs use?

  • Center origin, degrees
  • Bottom-left origin, radians
  • Top-left origin, radians
  • Top-left origin, degrees

Answer: Top-left origin, radians. Canvas places (0,0) at the top-left and arc angles are measured in radians.

Continue this course

Frequently asked questions

What does this checkpoint cover?

Everything from the async-and-browser-APIs stretch of the course: Promise combinators (all, allSettled, race, any), async iterators with for-await-of, AbortController for cancellation, Web Workers for background threads, IntersectionObserver and MutationObserver, IndexedDB for client-side storage, and the Canvas API for drawing. The build challenge combines several of them into one realistic data pipeline.

Which of these APIs only exist in the browser?

Worker, IntersectionObserver, MutationObserver, IndexedDB, and Canvas all require a browser environment. Promise combinators, async iterators/generators, and AbortController are pure JavaScript and run anywhere, including Node. That is why the runnable demos in this course model the browser-only APIs with plain-JS equivalents that share the same shape.

How should I use the checkpoint quiz?

Try to answer each question out loud before expanding it. If you can explain why — not just pick the right word — you have truly learned it. Anything you miss points you straight back to the lesson worth re-reading. The goal is recall and understanding, not just recognition.

Related lessons