// Hangs if you try to read and the stream closes (when readBlocks > streamBlocks)
const readBlocks = 4;
const streamBlocks = 3;
const blockSize = 1024;
const stream = makeStream(streamBlocks);
const reader = stream.getReader({ mode: "byob" });
let buffer = new Uint8Array(blockSize * readBlocks);
readAllBYOB(reader, buffer).then(([blocks, done]) => {
reader.releaseLock();
let byteLen = 0;
for(const block of blocks) {
byteLen += block.byteLength;
}
console.log("all done, bytes read:", byteLen, done);
});
function makeStream(loops) {
let totalBytesOutput = 0;
console.log("creating stream size:", loops * blockSize);
return new ReadableStream({
type: "bytes",
async start(controller) {
console.log(
`stream start- ${controller.constructor.name}.byobRequest = ${controller.byobRequest}`,
);
try {
const data = new TextEncoder().encode("s".repeat(blockSize));
totalBytesOutput += data.byteLength;
console.log("stream start- enqueuing, total:", data.byteLength, totalBytesOutput);
controller.enqueue(data);
} catch (err) {
console.error("stream start- error, closing", err);
controller.error(err);
}
},
async pull(controller) {
// ignoring actual byobReuest object
console.log(
`stream pull- ${controller.constructor.name}.byobRequest = ${controller.byobRequest}`,
);
try {
// Pretend we don't know when data runs out until the request is made.
// In BYOD mode, the read never returns. Unless you do one of the following:
// 1. Enqueueing data before calling close (but we don't have any to enqueue)
// 2. Call controller.error() instead, but that's ugly
if (totalBytesOutput >= blockSize * loops) {
console.log("stream pull- closing");
controller.close();
return;
}
const data = new TextEncoder().encode("p".repeat(blockSize));
totalBytesOutput += data.byteLength;
console.log("stream pull- enqueuing, total:", data.byteLength, totalBytesOutput);
controller.enqueue(data);
} catch (err) {
console.error("stream pull- error, closing", err);
controller.error(err);
}
},
});
}
async function readAllBYOB(reader, output) {
let targetBytes = output.byteLength;
let readBytes = 0;
let blocks = [];
let streamDone = false;
console.log('readAllBYOB- start: ', targetBytes);
while (readBytes < targetBytes) {
console.log('readAllBYOB- try reading:', output.byteLength);
// This does not return on the final read, even when stream is closed
let { done, value } = await reader.read(output);
console.log('readAllBYOB- read, done:', value?.byteLength, done);
streamDone = done;
if (value) {
blocks.push(value);
readBytes += value.byteLength;
}
if (done || !value) {
break;
}
if (readBytes < targetBytes) {
output = new Uint8Array(targetBytes - readBytes);
}
}
console.log(
'readAllBYOB- blocks, remainingBytes, done:',
blocks.length,
targetBytes - readBytes,
streamDone
);
return [blocks, streamDone];
}