const response = await fetch('/api/users');
const users = await response.json();
console.log(users);
Topics
37
ArrayBuffer, TypedArrays & Binary Data
Arrays & Array Methods
Async Iterators & Streams
Browser Storage & Web APIs
Classes & Class Syntax
Date, Time & Internationalization (Intl API)
Debugging, Testing & Tooling
Design Patterns in JavaScript
Destructuring, Spread & Rest
DOM & Events
Error Handling
ES Modules
Event Loop & Concurrency
Functional Programming
Iterators & Generators
JSON & Data Serialization
Map, Set, WeakMap & WeakSet
Memory Management & Garbage Collection
Networking: Fetch, XHR, WebSockets & CORS
Numbers, Math & BigInt
Objects, Property Descriptors & Immutability
Optional Chaining & Nullish Coalescing
Package Management, Bundlers & Transpilation (npm, Webpack/Vite, Babel)
Performance Optimization: Debouncing, Throttling & Memoization
Promises & async/await
Prototypes & Inheritance
Proxy & Reflect
Regular Expressions
Scope, Hoisting & Closures
Security: XSS, CSRF & Content Security Policy
Service Workers & Progressive Web Apps
Strings & Template Literals
Symbols & Well-Known Symbols
this & Binding
Types & Coercion
Web Components & Custom Elements
Web Workers & Multithreading
Networking: Fetch, XHR, WebSockets & CORS
10 questions found
fetch(url) returns a Promise that resolves to a Response object once headers arrive; you then call .json() (or .text(), etc.) — itself also returning a Promise — to read and parse the actual body.
Real-world example
Loading a list of products from an API when a page first renders.
JSON & Data Serialization
Pass an options object as the second argument specifying method: 'POST', a Content-Type header, and a JSON.stringify()'d body — fetch doesn't set the Content-Type header automatically for you.
await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Sam' })
});
Real-world example
Submitting a new user registration form's data to a backend API.
JSON & Data Serialization
fetch() only rejects for network-level failures (DNS errors, no connectivity, CORS block) — any response the server actually sends, including error status codes, resolves successfully. You must check response.ok (or response.status) yourself and throw manually if needed.
const res = await fetch('/api/missing');
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
}
const data = await res.json();
Real-world example
A bug where a failed API call with a 500 status was silently treated as success because .ok wasn't checked.
Error Handling
CORS (Cross-Origin Resource Sharing) is a browser security mechanism that blocks a web page from reading responses from a different origin (domain/port/protocol) unless the server explicitly allows it via response headers like Access-Control-Allow-Origin — this protects users from malicious sites silently reading data from other sites on their behalf.
// Server must respond with, e.g.:
// Access-Control-Allow-Origin: https://myapp.com
fetch('https://api.otherdomain.com/data'); // blocked unless server allows it
Real-world example
Debugging why an API call works from Postman but fails with a 'CORS policy' error only in the browser.
Security: XSS
CSRF & Content Security Policy
Create an AbortController, pass its .signal to fetch()'s options, and call controller.abort() to cancel the request — fetch's Promise then rejects with an AbortError, which you can distinguish from real failures in your catch block.
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.catch(err => { if (err.name === 'AbortError') console.log('cancelled'); });
controller.abort(); // cancels the request
Real-world example
Cancelling a previous search request when the user types a new character before the old one finishes.
Async Iterators & Streams
XHR is the older, event-based API for HTTP requests. Unlike fetch(), it natively supports upload/download progress events and can be aborted synchronously without needing the AbortController API — some libraries and progress-bar-driven upload UIs still rely on XHR for these reasons.
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.upload.onprogress = (e) => console.log(`${e.loaded}/${e.total}`);
xhr.onload = () => console.log(xhr.responseText);
xhr.send(formData);
Real-world example
Showing a real-time upload progress bar for a large file, which fetch() can't do natively for uploads.
Debugging
Testing & Tooling
A WebSocket establishes a single persistent, full-duplex connection after an initial HTTP handshake, allowing both client and server to send messages to each other at any time with minimal overhead — unlike fetch(), which requires a brand-new HTTP request/response cycle (with its own headers and TCP overhead) for every single exchange.
const socket = new WebSocket('wss://example.com/chat');
socket.onmessage = (event) => console.log('Received:', event.data);
socket.onopen = () => socket.send('Hello server!');
Real-world example
Building a live chat application or real-time collaborative editor where the server needs to push updates instantly.
Event Loop & Concurrency
For 'non-simple' requests (custom headers, methods like PUT/DELETE, or a Content-Type other than a few basic ones), the browser automatically sends an OPTIONS request first, asking the server whether the actual request is allowed — only if the server responds affirmatively does the browser send the real request.
// Browser automatically sends, before your actual PUT request:
// OPTIONS /api/users/1
// Access-Control-Request-Method: PUT
// Access-Control-Request-Headers: content-type, authorization
Real-world example
Understanding why a simple GET request works cross-origin without extra config, but a PUT with a custom Authorization header triggers an extra network round-trip.
Security: XSS
CSRF & Content Security Policy
SSE (via EventSource) provides a one-way stream of text events FROM server TO client over a single long-lived HTTP connection, automatically reconnecting on disconnect — simpler than WebSockets when you don't need the client to send messages back over the same connection, like live notifications or a streaming AI chat response.
const events = new EventSource('/api/notifications');
events.onmessage = (e) => console.log('New notification:', e.data);
Real-world example
Streaming a live AI-generated response token-by-token to the client, or pushing live notification counts.
Async Iterators & Streams
How would you implement request retries with exponential backoff and a total timeout using fetch()?
AdvancedCombine a retry loop around fetch() with AbortController-based per-attempt timeouts, increasing the delay between attempts exponentially, and stop retrying once either the attempt limit or overall elapsed time budget is exceeded.
async function fetchWithRetry(url, { retries = 3, timeout = 3000 } = {}) {
for (let i = 0; i <= retries; i++) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
try {
const res = await fetch(url, { signal: controller.signal });
clearTimeout(timer);
if (res.ok) return res;
} catch (err) {
if (i === retries) throw err;
await new Promise(r => setTimeout(r, 500 * 2 ** i));
} finally {
clearTimeout(timer);
}
}
}
Real-world example
Building a resilient API client that gracefully handles flaky mobile network connections.
Error Handling