Skip to content

Instantly share code, notes, and snippets.

@bokwoon95
Last active March 1, 2026 18:38
Show Gist options
  • Select an option

  • Save bokwoon95/cbf4fedefc9e2043ca5a820270659f28 to your computer and use it in GitHub Desktop.

Select an option

Save bokwoon95/cbf4fedefc9e2043ca5a820270659f28 to your computer and use it in GitHub Desktop.
streamResponseLines is a generator function that streams over a response body (returned by fetch()) line by line. Use it like this: `for await (const line of streamResponseLines(response)) { ... }`.
/**
* streamResponseLines is a generator function that streams over a response
* body (returned by fetch()) line by line. Use it like this:
* `for await (const line of streamResponseLines(response)) { ... }`.
*
* @param {Response} response
*/
async function* streamResponseLines(response) {
const reader = response.body.getReader();
// Assume the response body is in UTF-8 encoding.
const textDecoder = new TextDecoder("utf-8");
let line = "";
let chunk = new Uint8Array();
// Read from the response body in chunks.
for (let readResult = await reader.read(); !readResult.done; readResult = await reader.read()) {
if (chunk.length > 0) {
// We have a carryover chunk from a previous iteration. There are
// guaranteed to be no newlines inside since the previous iteration's
// chunk.indexOf(10) would have caught it.
//
// Stream option has to be true because we haven't encountered a
// newline yet, more data may be decoded for the current line.
line += textDecoder.decode(chunk, { stream: true });
}
// Get the reader's current chunk.
chunk = readResult.value;
// Jump to each newline '\n' byte in the chunk. 10 is the ASCII/UTF-8
// decimal value of the '\n' byte.
for (let index = chunk.indexOf(10); index >= 0; index = chunk.indexOf(10)) {
// We found a newline, decode everything up to this index and consider
// that as a complete line and yield it.
line += textDecoder.decode(chunk.subarray(0, index));
yield line;
// Reset the line.
line = "";
// Shorten the chunk to exclude what we have already decoded.
chunk = chunk.subarray(index + 1);
}
}
// Flush any remainder bytes in the chunk.
if (chunk.length > 0) {
line += textDecoder.decode(chunk);
yield line;
}
}
const response = await fetch("https://example.org/some/api/call");
for await (const line of streamResponseLines(response)) {
console.log(line);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment