=========================
As web developers, we're constantly looking for ways to improve user experience and performance. One of the key areas is handling large amounts of data, such as video streams or live updates, without overwhelming the client's resources. In this article, we'll explore a powerful technique: using fetch() with streaming responses.
The Problem
Traditional fetch() responses are typically delivered in a single chunk, which can lead to performance issues when dealing with large datasets. This is because the browser must buffer the entire response before rendering it, resulting in slow load times and increased memory usage.
Enter ReadableStream
To address this problem, modern browsers introduce the concept of ReadableStream, a low-level API for handling streaming data. By using fetch() with a ReadableStream destination, we can process large responses as they arrive, rather than waiting for the entire response to be buffered.
Code Example: Streaming Video
const video = document.getElementById('video');
const url = 'https://example.com/video.mp4';
// Create a ReadableStream destination
const reader = new ReadableStream().getReader();
// Use fetch with streaming response
fetch(url, { method: 'GET' })
.then(response => {
if (response.ok) {
// Set the response as the stream source
const read = async () => {
const { done, value } = await reader.read();
if (!done && value !== null) {
// Process the chunk of video data
console.log(value);
// Append to the video element's src attribute
video.srcObject = new MediaSource().addSourceBuffer('video/mp4; codecs="avc1.42E01E"').appendBuffer(new Uint8Array(value));
}
};
} else {
throw new Error(`Failed to fetch: ${response.statusText}`);
}
})
.catch(error => console.error('Error:', error));
In this example, we create a ReadableStream destination using the getReader() method. We then use fetch() with the streaming response option set to our stream reader object. As each chunk of data arrives, we process it by logging it and appending it to the video element's source buffer.
Progressive UI
By processing streaming responses in this way, you can create a progressive UI that loads content as it becomes available. This is particularly useful for applications like video players or live updates, where users can start interacting with data before the entire response has been received.
Code Example: Live Updates
const updates = document.getElementById('updates');
// Create a ReadableStream destination
const reader = new ReadableStream().getReader();
fetch('/live-updates')
.then(response => {
if (response.ok) {
// Set the response as the stream source
const read = async () => {
const { done, value } = await reader.read();
if (!done && value !== null) {
// Process each update chunk
console.log(value);
const updateElement = document.createElement('div');
updateElement.innerText = new TextDecoder().decode(new Uint8Array(value));
updates.appendChild(updateElement);
}
};
} else {
throw new Error(`Failed to fetch: ${response.statusText}`);
}
})
.catch(error => console.error('Error:', error));
In this example, we create a ReadableStream destination and use it with the /live-updates endpoint. As each chunk of data arrives, we process it by logging it and appending an update element to the page.
When NOT to Use It
While fetch() with streaming responses is incredibly powerful, there are cases where you should avoid using this technique:
- Small datasets: For small amounts of data, traditional
fetch()responses may be sufficient. - Legacy browsers: Older browsers may not support
ReadableStream, so you'll need to fall back to traditionalfetch()methods.
Related Apiary Lessons
If you're interested in learning more about advanced web development topics like streaming, consider the following Apiary lessons:
- "WebSockets: Real-time Communication with Web Browsers"
- "Server-Sent Events (SSE): Pushing Updates from Server to Client"
Conclusion
Browser fetch streaming is a game-changer for handling large amounts of data in web applications. By using ReadableStream destinations with fetch(), you can process responses as they arrive, improving user experience and reducing resource usage.
As the beekeeper's motto goes:
"Just like bees collect nectar from flowers, we collect knowledge from the web."