Checkpoint: Browser APIs

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.

Learn Checkpoint: Browser APIs in our free JavaScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

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.

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

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.

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 .

Up next: Final Project — put all of it to work. 🚀

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.