-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
211: Working with the Fetch API and Streaming Responses
I see this happen all the time during code reviews: a developer calls await fetch(url) and immediately assumes that because the promise resolved, the data is sitting there waiting for them. They treat the Response object like a completed package delivered to their door.
The Myth: "The Promise Resolves When the Data Arrives"
It's a subtle but critical misunderstanding. Let's look at why this is wrong. If you're fetching a 500MB JSON file, the fetch() promise doesn't wait for all 500 million bytes to travel across the ocean from the server to your browser. If it did, your app would feel frozen, and you'd have no way to show a progress bar.
// The common mistake: thinking the data is "done" here
const response = await fetch('https://api.example.com/massive-dataset');
console.log('Data is here!'); // WRONG. Only the headers are here.
At the moment that first await resolves, the browser has only received the HTTP headers. It knows the status code (200, 404, etc.) and the content type, but the actual body of the response is still streaming in over the network. If you try to use the response before consuming the body, you're essentially standing at the front door while the delivery driver is still unloading the truck.
The Reality: The Response is a Stream
To actually get the data, you usually call response.json() or response.text(). These methods are also asynchronous because they have to wait for the rest of the stream to finish. But here is the pro tip: you don't have to wait for the whole thing. You can hook directly into the response.body, which is a ReadableStream.
This is how modern AI chat interfaces work. When you see ChatGPT "typing" its response word-by-word, it isn't waiting for the entire paragraph to be generated. It's reading the response stream chunk by chunk as the server pushes them out.
Reading the Stream Manually
If you want to process data as it arrives—perhaps to update a progress bar or render text in real-time—you need to get a reader from the stream. I'll show you how to do this using a while loop. It looks a bit more verbose than .json(), but it gives you total control.
async function streamData(url) {
const response = await fetch(url);
const reader = response.body.getReader();
const decoder = new TextDecoder(); // Converts bytes to string
let done = false;
while (!done) {
const { value, done: doneReading } = await reader.read();
done = doneReading;
if (value) {
// 'value' is a Uint8Array containing a chunk of the response
const chunk = decoder.decode(value, { stream: true });
console.log('Received chunk:', chunk);
// Here is where you'd update your UI in real-time
}
}
console.log('Stream complete.');
}
A quick side note: TextDecoder is essential here. The stream gives you Uint8Array (raw bytes). If you just try to print those, you'll see a bunch of numbers. The decoder turns those bytes back into human-readable text. I've spent way too many hours debugging "weird numbers" in my console only to realize I forgot to decode the stream.
Using this pattern, you can handle massive files without crashing the browser's memory, because you're only holding one small "chunk" of the data at a time rather than the entire 500MB blob.
📋 Practical Task
Build a Real-Time Log Streamer
You are building a developer dashboard that monitors a server's logs. Instead of waiting for a massive log file to download completely, you need to display the logs line-by-line as they arrive from the server.
Requirements:
- Create a function
streamLogs(url, displayElement). - Use the
fetchAPI to connect to the provided URL. - Access the
response.bodyand use aReadableStreamDefaultReaderto read the data. - Use
TextDecoderto convert the chunks into strings. - Append each received chunk to the
displayElement(an HTML<div>) immediately, without overwriting the previous content. - Log "Log Stream Finished" to the console once the stream is fully consumed.
Hint: Use a mock streaming URL or a local server that sends data slowly to verify that your UI updates incrementally rather than all at once.
There are no comments for now.