Fetch Timeouts and Retries: Make Read Requests Predictable

Practical Web Development — Lesson 6 of 10 · Intermediate A loading spinner needs an exit path. A network request can stall, fail before a response arrives or return an HTTP error. This tutorial adds a per-attempt timeout and at most one retry for a read-only JSON request. It deliberately does not retry writes such as enrolment payments or progress changes. Build a bounded read helper Put this function in a browser module. Call it with a public same-origin JSON endpoint. A local test server with a delayed endpoint is useful for exercising timeout behaviour. async function readJson(url, timeoutMs = 5000) { for (let attempt = 0; attempt < 2; attempt++) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); let retry = false; try { const response = await fetch(url, { signal: controller.signal }); if (!response.ok) { const error = new Error('HTTP ' + response.status); error.retry...

localStorage Tutorial: Save a Course Shortlist Safely

Practical Web Development — Lesson 4 of 10 · Intermediate

A shortlist can help a learner remember courses between visits on the same browser. For a small non-sensitive list, localStorage is convenient. It is not a database shared across devices and it should not be the authority for enrolment or paid access. This exercise stores only known course IDs and treats everything read back as untrusted data.

Use a versioned key and validate the shape

Create shortlist.js with the following functions. Run the example on a local HTTP page, not by depending on file-URL storage behaviour.

const KEY = 'course-shortlist-v1';
const allowed = new Set(['html', 'css', 'js']);
function readShortlist() {
  try {
    const parsed = JSON.parse(localStorage.getItem(KEY) || '[]');
    if (!Array.isArray(parsed)) return [];
    return [...new Set(parsed.filter(id => typeof id === 'string' && allowed.has(id)))];
  } catch {
    return [];
  }
}
function saveShortlist(ids) {
  const clean = [...new Set(ids.filter(id => allowed.has(id)))];
  try {
    localStorage.setItem(KEY, JSON.stringify(clean));
    return { saved: true, ids: clean };
  } catch {
    return { saved: false, ids: clean };
  }
}
const current = readShortlist();
const result = saveShortlist([...current, 'html']);
console.log(result.saved ? 'Saved on this browser' : 'Storage unavailable');
console.log(result.ids);

Handle two kinds of failure

Parsing can fail because a value was edited, an earlier release wrote a different format, or the saved JSON is malformed. Storage access can also fail because the environment restricts it or the quota has been exceeded. A try/catch around both read and write keeps the rest of the page usable.

The write function returns whether persistence succeeded. A real interface should show that distinction. It can keep the selection in memory for the current visit while explaining that it will not survive reload. Avoid telling someone “Saved” when only the screen changed.

Store identifiers instead of duplicated course records

Saving a whole title and price can leave an old record on the device long after the catalogue changes. IDs let the current catalogue supply current display information. Filtering against known IDs also removes entries for courses no longer offered. A larger application may need a migration policy rather than simply discarding unknown IDs.

Keep the trust boundary clear

Any script running on the same origin may be able to access this storage. Do not put passwords or secret tokens in the shortlist record. A stored ID can control which cards look selected; it cannot grant access on the server. Users can clear browser data at any time, so the app must tolerate an empty list.

Try deliberate failure cases

  1. Save HTML twice; the returned list should contain it once.
  2. Use the browser storage inspector to replace the JSON with invalid text. Reload without crashing.
  3. Store an object instead of an array; expect an empty list.
  4. Store an unknown course ID and confirm it is filtered out.

Practice

Add a Remove action and an explicit Clear shortlist button. Change only this key; do not call localStorage.clear(), which would erase unrelated preferences on the origin. If you support multiple tabs, listen for storage events and decide how the interface should reconcile changes.

Continue learning

Review our semantic HTML project, JavaScript course filter and course platform architecture.

Reference: Technical documentation for this lesson. Examples are learning exercises; test them in your environment.

Comments

Popular posts from this blog

What Is Multimedia? Examples and a Beginner Course Guide

Internet vs World Wide Web: HTML and Multimedia Explained

Video in Multimedia: Formats, Compression and Captions