LEARN TO CODE | JAVASCRIPT | 04 APIS AND MINI PROJECTS
04 APIs and mini projects: challenges
Demonstrate asynchronous success and failure, protected quiz scoring and honest browser persistence with reproducible test evidence.
START HERE | USE THE COMPLETE PROJECTS
Download the complete example projects . Open one folder at a time in VS Code. Each has index.html, script.js and style.css; the fetch projects include their JSON data.
Serve the project over local HTTP for fetch and reliable storage testing. If your VS Code setup has a teacher-approved preview extension, use its preview command. With Live Server installed, right-click index.html and choose Open with Live Server. Keep the same hostname and port when testing saved data.
The example code runs in the browser and does not require Node.js. Opening a file URL is not the right workflow for the JSON fetch project.
Attempt the tasks before revealing guidance. Record the action/input, expected result, actual output and any stored effect. Completion ticks last while this page is open.
CHALLENGE 01 | PREDICT THE TIMELINE
Run the complete 04.1 demo and explain the order of log entries. Use the second button during the wait.
Need a hint?
Find the point where the async function reaches await.
Reveal guidance
The function logs entry 1, then awaits its timer promise. The listener continues with entry 2. Fulfilment or rejection produces entry 3, then finally produces entry 4. Other event callbacks can run while it awaits.
CHALLENGE 02 | HANDLE THE REJECTION
Select Simulate failure and confirm Start becomes usable again. Explain the difference between catch and finally.
Need a hint?
One handles the rejected outcome; one restores state on either route.
Reveal guidance
catch displays the simulated failure. finally releases the running flag and disabled button. The same promise does not later become successful; a new click starts a new attempt.
CHALLENGE 03 | READ A REAL HTTP RESPONSE
Serve the 04.2 project over HTTP and load clubs.json. Inspect URL, status and body in Developer Tools.
Need a hint?
The returned Response is not the final array.
Reveal guidance
fetch yields a Response. Check ok, then await json and validate the parsed records. Only then render the three clubs with safe text nodes.
CHALLENGE 04 | SEPARATE THREE FAILURES
Test the missing-file, malformed-JSON and wrong-shape choices. Record the stage at which each fails.
Need a hint?
HTTP response, parsing and app validation are distinct.
Reveal guidance
A normal missing file gives a non-2xx response rejected by the explicit ok check. Malformed JSON fails json parsing. The wrong-shaped object parses but fails isClubList. Check your actual server’s response because fallback pages can use status 200.
CHALLENGE 05 | KEEP THE EMPTY STATE
Load empty.json after a successful populated list.
Need a hint?
Clear old output and treat [] according to the contract.
Reveal guidance
No old club items remain. The empty list is valid and the status says it loaded successfully with no clubs. Finally must still restore the button and aria-busy state.
CHALLENGE 06 | GUARD LOADING
Explain how the loading flag prevents overlapping loads and how the timeout is cleaned up.
Need a hint?
Look at both the guard and finally.
Reveal guidance
A new invocation returns while loading is true. The button is also disabled for normal use. AbortController cancels an unfinished operation after the timer fires; finally clears the timer and restores state. This is not a complete search-as-you-type race strategy.
CHALLENGE 07 | PROTECT THE QUIZ SCORE
Complete one correct answer, then attempt to submit the same question again. Check without a selection on the next question.
Need a hint?
The answered boolean is the logic guard.
Reveal guidance
The repeated submission does not add another point. A missing selection gives guidance and leaves answered false. Each authored question has one correct option and one scoring opportunity.
CHALLENGE 08 | FINISH AND RESTART
Complete an all-correct attempt, restart and complete an all-wrong attempt.
Need a hint?
Reset all state and rebuild enabled options.
Reveal guidance
The summaries should show 5/5 and 0/5. Restart returns to question 1 and clears the score. No out-of-range question should be read after the final answer.
CHALLENGE 09 | TARGET TASKS BY ID
Add two tasks, delete the first and add another. Mark one remaining task complete.
Need a hint?
Indexes shift; stable IDs identify records.
Reveal guidance
The intended row is removed, new IDs do not collide and counts match completion. The checkbox updates the task record and the visible span without replacing its focused control.
CHALLENGE 10 | DISTINGUISH MEMORY FROM SAVED DATA
Add a task, verify a successful save, reload and restore it. Then simulate a storage write failure.
Need a hint?
Visible output does not prove the write succeeded.
Reveal guidance
Successful changes survive reload. Failed writes leave the page’s current task visible but show a not-saved warning. Reload may restore the older saved list; report that limit honestly.
CHALLENGE 11 | RECOVER FROM BAD SAVED DATA
In your own local project, corrupt only csc-js04-tasks-v1 or introduce duplicate task IDs. Add a temporary task, then use Forget the saved copy.
Need a hint?
Do not overwrite unreadable saved data automatically.
Reveal guidance
Loading warns and disables persistence. Adding a temporary task keeps the unreadable entry intact. Forget removes this key only and re-enables saving; tasks stay in memory, and the next task change saves the current list again.
CHALLENGE 12 | CAPSTONE: FETCH THE QUESTION BANK
Build the optional 05-fetched-quiz project. Fetch questions.json, validate every question before starting, show a retry state on failure and preserve the single-answer scoring rules.
Need a hint?
Start with the completed quiz, replacing its fixed question array with a loaded bank.
Reveal guidance
The complete download contains the working integration. Its validation checks a non-empty bounded array, prompt/options text, an in-range integer answer index and explanation text. It cannot prove that the authored correct answer is factually correct; review the questions too.
The loader added to the quiz
const loadButton = document.querySelector("#quiz-load");
let bankLoading = false;
function validQuestionBank(value) {
return Array.isArray(value) && value.length >= 1 && value.length <= 20 && value.every((q) =>
q !== null && typeof q === "object" && !Array.isArray(q)
&& typeof q.prompt === "string" && q.prompt.trim() !== "" && q.prompt.length <= 250
&& Array.isArray(q.options) && q.options.length >= 2 && q.options.length <= 6
&& q.options.every((option) => typeof option === "string" && option.trim() !== "" && option.length <= 150)
&& Number.isInteger(q.answer) && q.answer >= 0 && q.answer < q.options.length
&& typeof q.explanation === "string" && q.explanation.trim() !== "" && q.explanation.length <= 500
);
}
async function loadQuiz() {
if (bankLoading) return;
bankLoading = true;
loadButton.disabled = true;
form.hidden = true;
summary.hidden = true;
progress.textContent = "Loading question bank...";
feedback.textContent = "";
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);
try {
const response = await fetch("./questions.json", { signal: controller.signal });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const value = await response.json();
if (!validQuestionBank(value)) throw new Error("Invalid question bank");
questions = value;
index = 0;
score = 0;
form.hidden = false;
loadButton.hidden = true;
renderQuestion();
optionsBox.querySelector("input").focus();
} catch (error) {
progress.textContent = "Question bank unavailable.";
feedback.textContent = "Check the local server, HTTP response and JSON structure, then try loading again.";
loadButton.textContent = "Retry loading quiz";
loadButton.hidden = false;
} finally {
clearTimeout(timeout);
bankLoading = false;
loadButton.disabled = false;
}
}
form.hidden = true;
progress.textContent = "Select Load quiz to begin.";
loadButton.addEventListener("click", loadQuiz);
This loader is not a standalone script. In the complete project, questions is declared with let as an empty array, the form starts hidden, a Load quiz button is present, and the original immediate renderQuestion() call is removed. The download includes those changes together.
Test valid loading, a 404 or server fallback, malformed JSON, an out-of-range answer index, retry after repair and a complete restarted attempt. The question bank is browser-visible practice data, not a hidden examination mark scheme.
FINISH | NEXT STEPS
Choose one extension with a clear purpose: a validated fetched quiz bank, a remaining-only task view, or tests that repeat the same critical interactions after each edit. Keep calculations, data handling and rendering separate enough to explain.
For shared accounts, secret credentials or durable multi-user records, the next step is an appropriate backend and access model. Browser code and localStorage alone do not provide that system.
KEEP LEARNING
Previous page JavaScript course overview
Download Section 04 example programs
Further reading: Promises · Fetch · JSON · Browser storage