spirobel
spirobel•5mo ago

How to feed a ReadableStream / async Generator into a Response object?

`` Bun.serve({ async fetch(req) { return new Response( async function* stream() { // ~~~~~~~^? yield "Hello, "; yield Buffer.from("world!"); }, ); }, }); it is possible to feed an async generator directly into the fetch Response with Bun. With deno this does not work. I also tried to turn it into a ReadableStream with ReadableStream.from(), but no success so far. Is this the correct way to produce a ReadableStream from an async generator, or do I have to do this differently? thanks!
No description
5 Replies
spirobel
spirobel•5mo ago
another try using the node:stream Readable. works in bun does not work in deno
spirobel
spirobel•5mo ago
No description
zamfofex
zamfofex•5mo ago
If you want to be able to mix and match strings and buffers, you need a preprocessor function like this:
// mix strings and buffers

Deno.serve(request => new Response(ReadableStream.from(preprocess(request))))

let encoder = new TextEncoder()

async function * preprocess(request)
{
for await (let chunk of makeStream(request))
{
if (typeof chunk === "string") chunk = encoder.encode(chunk)
yield chunk
}
}

async function * makeStream(request)
{
yield "Hello, "
yield encoder.encode("world!")
}
// mix strings and buffers

Deno.serve(request => new Response(ReadableStream.from(preprocess(request))))

let encoder = new TextEncoder()

async function * preprocess(request)
{
for await (let chunk of makeStream(request))
{
if (typeof chunk === "string") chunk = encoder.encode(chunk)
yield chunk
}
}

async function * makeStream(request)
{
yield "Hello, "
yield encoder.encode("world!")
}
Otherwise, you can get away with only buffers or only strings more easily:
// only buffers
Deno.serve(request => new Response(ReadableStream.from(makeStream(request))))

let encoder = new TextEncoder()

async function * makeStream(request)
{
yield encoder.encode("Hello, ")
yield encoder.encode("world!")
}
// only buffers
Deno.serve(request => new Response(ReadableStream.from(makeStream(request))))

let encoder = new TextEncoder()

async function * makeStream(request)
{
yield encoder.encode("Hello, ")
yield encoder.encode("world!")
}
// only strings
Deno.serve(request => new Response(ReadableStream.from(makeStream(request)).pipeThrough(new TextEncoderStream())))

async function * makeStream(request)
{
yield "Hello, "
yield "world!"
}
// only strings
Deno.serve(request => new Response(ReadableStream.from(makeStream(request)).pipeThrough(new TextEncoderStream())))

async function * makeStream(request)
{
yield "Hello, "
yield "world!"
}
spirobel
spirobel•5mo ago
thanks a lot for your reply! I didn't know yielding buffers or strings mattered. this is great info! is there something special I need to do to flush the buffers after every yield? I tried this:
javascript
Deno.serve(
(request) =>
new Response(
ReadableStream.from(makeStream(request)).pipeThrough(
new TextEncoderStream()
)
)
);
const wait = (n: number) => new Promise((resolve) => setTimeout(resolve, n));

async function* makeStream(request) {
yield "Hello,";
console.log("start wait ");
await wait(5000);
yield "world!";
}
javascript
Deno.serve(
(request) =>
new Response(
ReadableStream.from(makeStream(request)).pipeThrough(
new TextEncoderStream()
)
)
);
const wait = (n: number) => new Promise((resolve) => setTimeout(resolve, n));

async function* makeStream(request) {
yield "Hello,";
console.log("start wait ");
await wait(5000);
yield "world!";
}
it waited 5 seconds and then sent the whole Hello world. I thought it would send Hello, then wait for 5 seconds and then send the world! 😀 (while sending the Hello instantly) EDIT: with { headers: { "content-type": "text/html; charset=utf-8" } } it works