A real page may need to wait for a response while the learner keeps using it.
This lesson introduces promises, async/await, HTTP responses, JSON parsing, cancellation and result ownership.
Our task is a workshop-catalog loader with clear idle, loading, success, empty, error and canceled outcomes.
You will distinguish a network failure from an HTTP error, validate parsed data before rendering and prevent an older response from replacing a newer request’s result.
⏳
Async flow
Explain when code waits and when other work can proceed.
📨
Responses
Check HTTP status before interpreting a body.
✅
Data contract
Separate valid JSON from acceptable workshop records.
🧭
Ownership
Let only the newest request update the current result.
02
⏱️Asynchronous Work and the Event Loop
JavaScript code in a browser context generally runs each job to completion before another job runs on that same thread.
Browser facilities can handle waiting work such as timers and network operations, then arrange for callbacks or promise reactions to run later.
Asynchronous does not mean every line runs simultaneously or that heavy JavaScript computation automatically moves to another thread.
A long synchronous loop can still block interaction and rendering.
Awaiting a promise yields control from the async function while it is suspended; it does not freeze the whole browser.
Promise reactions run as microtasks.
A zero-delay timer queues later work; it does not guarantee that its callback runs immediately or at an exact wall-clock time.
console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
console.log("D");
// In this simple example: A, D, C, B
The ordinary logs run first.
The promise reaction is processed before the later timer task in this example.
Do not generalize this small trace into a promise about the timing of every network response.
03
🔔Callbacks — A Starting Point
A callback is a function supplied to another operation.
Event listeners from the previous lesson are callbacks.
A timer callback receives control after the timer becomes eligible and the eventloop can run it.
Not every callback is asynchronous: Array.map calls its callback synchronously.
Judge the operation’s contract rather than assuming that passing a function always schedules it for later.
Several nested dependent callbacks can make success and failure paths difficult to follow.
Promises provide a composable representation of an eventual outcome.
The later ownership example adds an ID guard before changing the UI in finally.
Handle expected cancellation separately from a request error that the learner should retry.
08
🌐HTTP and an API Response Contract
An API exposes an agreed interaction: endpoint, method, headers, request data and response format.
A client should know which fields and types the response is expected to contain.
HTTP status and JSON structure answer different questions.
A 200-range status commonly represents HTTP success.
A 404 response means the requested resource was not found; a 500 response indicates a server-side error class.
The appropriate interface response depends on the API’s documented contract.
An empty JSONarray can be a successful result with no workshops.
A malformed body or unacceptable record shape is a different outcome.
A 204 response has no content, so do not blindly demand a JSON body for every successful status.
Separate response decisions
📨Transport
Did the client receive a response or encounter a failure?
🏷️HTTP status
Does the status meet the operation’s contract?
🔤Body format
Can the promised representation be parsed?
✅Record contract
Are the parsed fields and values acceptable?
09
📨Fetch — Check the Response Before Parsing
Fetch returns a promise for a Response.
HTTP error statuses such as 404 do not normally reject that promise by themselves.
Check response.ok or the expected status, then choose how to handle the response.
A network or browser access failure can reject fetch without a usable response.
An application can also fail later during body reading, parsing or validation.
Keep these stages distinct while debugging.
Response.ok is true for statuses 200 through 299.
A body-reading method such as json is asynchronous and normally consumes the body stream.
Do not assume you can read the same body repeatedly without cloning or another deliberate strategy.
const response = await fetch("/api/workshops", {
headers: { Accept: "application/json" }
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
This request header communicates a preferred response representation; it does not force the server to return valid JSON or the expected fields.
The next step still needs validation.
10
✅JSON Parsing Is Not Schema Validation
Json parses a response body into a JavaScript value.
Valid JSON can represent a string, number, null, object or array.
Parsing successfully does not prove that a value is the catalog you expected.
Our catalog contract is a dense array with at most 20 records.
Each record has a unique non-empty string ID of at most 40 characters, a non-empty title of at most 80 characters, and integer capacity/reserved values from 0–1000 with reserved not exceeding capacity.
IDs and titles are trimmed before uniqueness and rendering checks.
Reject the whole payload if the contract fails.
Avoid displaying an invalid availability count or silently turning an absent capacity into zero.
Derive remaining only from accepted numeric fields.
if (!Array.isArray(data)) {
throw new Error("Expected a workshop array");
}
for (const workshop of data) {
if (!Number.isInteger(workshop.capacity) ||
!Number.isInteger(workshop.reserved) ||
workshop.reserved > workshop.capacity) {
throw new Error("Invalid workshop record");
}
}
This abbreviated snippet illustrates the idea.
The lab’s validator also checks lengths, lower/upper bounds, uniqueness and sparse slots before rendering its predefined payload.
11
💬UI States — Loading Is Part of the Task
A useful loader begins idle, shows loading while work is pending, and ends with success, empty, error or canceled.
Clear stale results when starting a new request, or deliberately label them as old if the product chooses to retain them.
Use a concise status and an appropriate busy state for the result region.
An empty result should explain that no workshops were returned; it should not look like an unfinished loading screen.
Keep a retry action available after a recoverable failure.
The lab keeps Load enabled so you can start overlapping requests deliberately.
Cancel applies to the newest pending request.
Its request log explains earlier completions that were ignored.
⏳
Loading
A pending response is owned by the newest request.
📚
Success / empty
Accepted records or a clear zero-record outcome.
⚠️
Error
HTTP, transport, parsing or schema failure.
✋
Canceled
The active request was intentionally aborted.
12
✋AbortController and Timeouts
AbortController provides a signal that compatible operations can observe.
Pass its signal to fetch, then call abort when the result is no longer needed.
The operation can reject with an AbortError; cancellation is different from reporting a server failure.
Aborting does not guarantee that a server never received or processed a request.
For a real write operation, retry and cancellation policies need the server’s documented behavior.
A read-only catalog has different consequences from creating a payment or reservation.
A timeout can be implemented by aborting after a chosen deadline and clearing the timer in finally.
Record whether the deadline or the learner initiated cancellation if those outcomes need different messages.
These helper names illustrate two independent API tasks.
Their endpoints and failure policies must be implemented by the application.
A group await changes coordination, not the server’s capacity or authorization rules.
15
🔐CORS, Credentials and Safe Rendering
Cross-origin browser requests are governed by access rules such as CORS.
The server supplies the appropriate response headers; client JavaScript cannot simply grant itself permission.
A no-cors request can yield an opaque response that the script cannot read as ordinary JSON, so it is not a repair for an API that needs readable data.
Credentials handling depends on the request options, origins and server policy.
Keep private API keys and server secrets out of downloadable frontend files.
A browser-visible key is not made private by placing it in an external JavaScript file.
Treat response text as data when rendering labels.
Create elements and use textContent rather than inserting server-provided strings as raw HTML.
Keep failures understandable without exposing internal server traces to a learner.
const title = document.createElement("h4");
title.textContent = acceptedWorkshop.title;
card.append(title);
The local lab renders only accepted records with textContent and uses predefined response scenarios.
It does not contact external endpoints or store catalog data between visits.
16
🔄Premium Visualizer — Async Response Journey
Follow one fixed successful response through loading, status, parsing, validation and rendering. Use the approved timeline and playback controls. The live lab separately exercises success and failure paths with local simulated responses.
ASYNC API RESPONSE TRACE
Step 1 of 5
⏳
STEP 01
Start and own a catalog request
Assign a request ID and show loading while the promise is pending.
requestId = ++latestRequestId
What is happening?
Only this request may write the current result while it remains the latest owner.
17
🧪Premium Interactive — Async Catalog Loader
This is a local response simulation.
It uses real promises, timers, AbortController and Response.json with predefined bodies; it does not call a live API.
Choose a scenario and delay, then Load.
Inspect the state, accepted cards, payload and request log.
Try success, an empty catalog, HTTP 404, network failure, malformed JSON and invalid record shape.
Cancel while the newest request is loading.
Reset cancels pending work and returns to idle.
The timer delay is a teaching setting, not an exact timing guarantee.
For a race, start slow success at 1500ms, then switch to fast empty at 500ms and Load again.
The newest empty result should remain after the earlier success completes.
The log identifies the obsolete result as ignored.
You can also start a fast earlier request and a slow newer one to check that old cleanup does not clear the newer loading state.
Idle. Choose a simulated response and load it.
Accepted catalog cards
Accepted payload
No accepted payload yet.
Request log
[]
18
🛠️Debugging — Locate the Failed Stage
⏳
Pending
Check whether the awaited operation ever settles and whether cancellation is wired.
📨
Response
Separate a rejected request from a received error status.
✅
Data
Inspect body parsing and the accepted record contract.
🧭
Ownership
Check every UI write, including catch and finally.
If a 404 still reaches a success callback, inspect response.ok rather than assuming fetch rejects every unsuccessful HTTP status.
If JSON parsing fails, inspect the body and representation; the server may have returned an HTML error page.
If an older result replaces a newer query, check its request ID before rendering.
If loading disappears while the latest request is pending, inspect an older finally block.
If cancellation leaves an unhandled rejection, make sure the canceled operation still has a rejection handler.
If a CORS failure appears, inspect the server’s access policy rather than attempting to read an opaque no-cors response.
Use the deterministic lab cases to isolate one stage.
A malformed body should fail parsing; an over-reserved record should fail validation.
Neither should leave accepted cards visible under the new error state.
19
💬Interview Questions — Flip to Explain
QUESTION
Does async automatically move heavy code off the main thread?
Click or press Enter to explain
ANSWER
No. An async function can still perform blocking synchronous work. Await suspends that function while an awaited promise is pending.
QUESTION
What does an async function return?
Click or press Enter to explain
ANSWER
A promise. A returned value fulfills it; an uncaught error rejects it.
QUESTION
Does fetch reject just because HTTP status is 404?
Click or press Enter to explain
ANSWER
Normally no. It can fulfill with a Response whose ok value is false. Inspect the status explicitly.
QUESTION
Why await response.json separately?
Click or press Enter to explain
ANSWER
Reading and parsing the response body is asynchronous and can fail independently of receiving the response.
QUESTION
Does valid JSON prove the catalog record shape is valid?
Click or press Enter to explain
ANSWER
No. Validate types, bounds, unique IDs and relationships according to the application contract.
QUESTION
Why guard finally with the request ID?
Click or press Enter to explain
ANSWER
An older request must not clear a newer request’s loading state or change its current UI outcome.
QUESTION
Does Promise.all cancel other inputs when one rejects?
Click or press Enter to explain
ANSWER
No. Rejection of the group does not automatically cancel already-started operations.
QUESTION
What should an empty successful response show?
Click or press Enter to explain
ANSWER
A clear zero-record result and a finished loading state, rather than an error or perpetual spinner.
20
❓MCQ Practice — Predict the Async Outcome
PRACTICE
1. What does an async function return?
PRACTICE
2. Which order fits the simple A/timer B/promise C/D example?
PRACTICE
3. What should a promise chain return when the next step reads JSON?
PRACTICE
4. How does fetch normally handle a received HTTP 404?
PRACTICE
5. What does response.ok represent?
PRACTICE
6. What can response.json() do after a successful HTTP status?
PRACTICE
7. What is required after successfully parsing a catalog body?
PRACTICE
8. Which API supplies a signal for cancellation?
PRACTICE
9. Why can an older finally block cause a UI bug?
PRACTICE
10. In what order does Promise.all supply fulfilled group results?
PRACTICE
11. What happens to other started operations when Promise.all rejects?
PRACTICE
12. What should the latest successful empty catalog show?
21
💻Extra Practice — Reproduce Specific Async Outcomes
📚
Success
Load success. Expected: four accepted records with remaining counts 18, 0, 30 and 12; total 60.
📭
Empty
Load empty. Expected: no cards, accepted [] and a completed empty status.
🏷️
HTTP error
Load HTTP 404. Expected: status-stage failure; no accepted payload.
🔤
Parse failure
Load malformed JSON. Expected: parsing failure after the successful response status.
✅
Contract failure
Load invalid record shape. Expected: validation rejection before cards are rendered.
✋
Cancel
Cancel during the scheduled delay. Expected: canceled state, busy false and no accepted cards.
🧭
Slow old result
Start slow success then fast empty. Expected: newest empty persists; old success is logged as ignored.
🔄
Old cleanup
Start fast success then slow empty.
Expected: loading stays true for the newer request after the old completion; finally the newest empty appears.
22
📌Quick Revision
⏳Async: Waiting can yield control; heavy synchronous work still blocks.
📦Promise: Track eventual fulfillment or rejection.
🔗Chain: Return the next promise to connect its outcome.
🧵Await: Suspend the async function, not the whole browser.
📨Fetch: Check status before reading the body.
✅Contract: Parsing and validation are separate stages.
💬UI: Idle, loading, success, empty, error and canceled are explicit.
✋Abort: Handle cancellation and release timers/listeners.
🧭Ownership: Guard success, failure and cleanup writes.
🤝Groups: Choose sequential or grouped work deliberately.
23
📖Glossary — Flip to Learn
TERM
Promise
Click to see meaning
DEFINITION
Promise
A representation of an eventual fulfillment value or rejection reason.
TERM
Pending
Click to see meaning
DEFINITION
Pending
The promise has not yet settled into fulfillment or rejection.
TERM
Fulfillment
Click to see meaning
DEFINITION
Fulfillment
Successful settlement with a value.
TERM
Rejection
Click to see meaning
DEFINITION
Rejection
Unsuccessful settlement with a reason.
TERM
Await
Click to see meaning
DEFINITION
Await
An operation that suspends an async function until the awaited outcome is available.
TERM
Response
Click to see meaning
DEFINITION
Response
The Fetch API representation of a received response and its body access.
TERM
JSON
Click to see meaning
DEFINITION
JSON
A text data representation that can be parsed into JavaScript values.
TERM
Schema validation
Click to see meaning
DEFINITION
Schema validation
Checking a parsed value against an expected data contract.
TERM
Abort signal
Click to see meaning
DEFINITION
Abort signal
A signal that compatible work observes to support cancellation.
TERM
Request ownership
Click to see meaning
DEFINITION
Request ownership
A rule deciding which asynchronous operation may update the current UI.
24
🏆Final Challenge — A Loader with Owned Outcomes
Write an asynchronous catalog loader around an implemented endpoint or the lesson’s local response adapter.
Show idle/loading/success/empty/error/canceled clearly.
Check the HTTPcontract, await body parsing and validate accepted records before rendering them as text.
Use an AbortController for compatible cancellation and increasing request IDs to protect every UI write.
Keep older completion and cleanup from replacing the newest result.
Reset or page cleanup should cancel pending work and invalidate it without leaving an unhandled rejection.
Test all six response scenarios, cancellation, reset while pending and both request-order races.
Record which stage fails and whether the newest busy state remains correct.
Use a timer-controlled test for logic, then inspect real browser timing, controls and accessibility feedback.
Success criterion: explain the promise path, distinguish transport/HTTP/parse/schema outcomes and demonstrate stable result ownership.
Level 19 will introduce browser storage and other web APIs.
25
✅Level 18 Complete?
Before marking complete, explain an awaited response, handle one failure at each relevant stage and demonstrate cancellation plus both overlapping-request cases. Completion is a local study marker rather than an assessment score.