Skip to content
tezvyn:

Download a large file via fetch and track ReadableStream progress

Source: developer.mozilla.orgHardHow cards are made

Download a large file via fetch and track ReadableStream progress

It tests streaming I/O and backpressure beyond Promise-based fetch. Acquire a reader from response.body, loop read() until done, sum chunk lengths against Content-Length, and emit progress.

What's really being asked

The interviewer wants to know if you can work with low-level streaming primitives instead of high-level convenience methods. Fetch returns a ReadableStream in response.body, and consuming it correctly requires understanding readers, chunk-based iteration, stream locking, and backpressure. Senior candidates should demonstrate that they know how to avoid blocking the main thread or exhausting memory when handling multi-gigabyte payloads, and they should mention that ReadableStream implements the async iterable protocol as an alternative to manual reader management.

The full answer

A strong response walks through five steps in order. First, validate the response status and read the Content-Length header to establish the total byte budget. Second, obtain a reader by calling response.body.getReader, which locks the stream so no other consumer can interfere. Third, create a read loop that repeatedly awaits reader.read and destructures the done boolean and value Uint8Array. Fourth, accumulate the byte count from each chunk length, calculate the ratio against Content-Length, and push that percentage to the UI via callbacks or a reactive stream. Fifth, handle cleanup by releasing the reader lock when the loop terminates or when an abort signal fires, and surface any network or decoding errors to the user. Mentioning the for await of syntax as a modern alternative shows extra depth, since ReadableStream is async iterable.

The mistakes people make

The biggest red flag is immediately calling response.text, response.blob, or response.arrayBuffer. These methods consume the entire stream internally and give you no hook for per-chunk progress. Another mistake is forgetting that getReader locks the stream; attempting to pipe or tee afterward throws a TypeError. Some candidates also ignore backpressure by processing chunks synchronously inside the read loop without yielding to the event loop, which can jank the UI on large files. Finally, failing to check Content-Length before calculating progress leaves you without a denominator, and neglecting to handle reader cancellation means the download continues in the background even after the user navigates away.

What usually comes next

The interviewer may ask how you would handle a response that uses chunked transfer encoding without a Content-Length header. They might also probe cancellation by asking how to abort the download mid-stream using an AbortController and reader.cancel. Another variant is asking how to tee the stream so you can simultaneously show progress and write the file to disk via a WritableStream or FileSystemWritableFileStream, or how to transform chunks on the fly using TransformStream.

A concrete example

Imagine fetching a 500 MB video file. You call fetch with an AbortController signal, then check the Content-Length header to get roughly 524288000 bytes. You lock the stream by assigning response.body.getReader to a reader variable. Inside a while loop, you destructure done and value from awaiting reader.read. If value exists, you add value.length to a received variable and set progress to received divided by total. You update a progress bar element each iteration. When done is true, you close the reader. If the user clicks cancel, you call controller.abort and await reader.cancel to free the underlying connection.

Interview question

What is the immediate consequence of calling response.body.pipeTo() after already obtaining a reader from that same body?

  • a.A TypeError is thrown because the stream is locked to the readerCorrect
  • b.The reader is forcibly released so the pipeTo can take over
  • c.The pipe operation is queued until the reader releases its lock
  • d.The stream data is automatically teed so both consumers receive identical chunks
Why?

Calling getReader() locks the ReadableStream, and the card explicitly states that any subsequent attempt to pipe or tee it will throw a TypeError. Option C is a tempting distractor because queuing feels intuitive, but locked streams fail immediately rather than deferring the pipe operation.

Just read this? Test yourself on what you have been reading.

Read the original → developer.mozilla.org

You just looked this up. Could you explain it out loud?

That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Every open role lists the topics its interview covers, so you can prepare for the real thing rather than guessing.

See open roles