27. fetch and XMLHttpRequest: requests, streaming, aborting
fetch() is promise-based and the modern default; XMLHttpRequest (XHR)
predates promises and is still relevant for upload progress events,
which fetch didn't support natively until ReadableStream request bodies
matured.
// Aborting an in-flight fetch — critical for search-as-you-type, tab switches, etc.
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5s timeout
try {
const res = await fetch("/api/search?q=js", { signal: controller.signal });
clearTimeout(timeoutId);
const data = await res.json();
} catch (err) {
if (err.name === "AbortError") console.log("Request timed out or was cancelled");
else throw err;
}
Streaming a response body — process data as it arrives instead of waiting for the whole payload (large JSON, NDJSON logs, LLM token streams):
const res = await fetch("/api/stream");
const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log("chunk:", decoder.decode(value, { stream: true }));
}