Openstatus
www.openstatus.dev
1export async function* yieldMany(promises: Promise<unknown>[]) {
2 // Attach .then() handlers to the promises to remove them as soon as they resolve
3 // biome-ignore lint/complexity/noForEach: REMINDER: do not use for await (const p of promises) as it will not work as expected
4 promises.forEach((p) => {
5 p.then((value) => {
6 promises.splice(promises.indexOf(p), 1);
7 return value;
8 });
9 });
10
11 // Continue yielding the results of the promises as they resolve
12 while (promises.length > 0) {
13 yield await Promise.race(promises);
14 }
15
16 return "done";
17}
18
19export function iteratorToStream(iterator: AsyncGenerator) {
20 return new ReadableStream({
21 async pull(controller) {
22 try {
23 const { value, done } = await iterator.next();
24 if (done) {
25 controller.close();
26 } else {
27 controller.enqueue(value);
28 }
29 } catch (err) {
30 console.error("Stream error:", err);
31 controller.error(err);
32 }
33 },
34 });
35}
36
37const encoder = new TextEncoder();
38const decoder = new TextDecoder();
39
40/**
41 * HOW TO USE IT IN YOUR ROUTE
42 * @returns {Response}
43 */
44export async function POST(request: Request) {
45 // extract your params from the request
46 const _json = await request.json();
47
48 const generator = yieldMany([
49 new Promise((resolve) =>
50 setTimeout(() => resolve(encoder.encode("1")), 200),
51 ),
52 new Promise((resolve) => resolve(encoder.encode("2"))),
53 new Promise((resolve) =>
54 setTimeout(() => resolve(encoder.encode("3")), 500),
55 ),
56 ]);
57
58 const stream = iteratorToStream(generator);
59 return new Response(stream);
60}
61
62/**
63 * HOW TO USE IT IN YOUR CLIENT
64 */
65async function clientConsumeStream() {
66 const response = await POST(new Request("")); // fetch("/api/path/to/route", { method: "POST" });
67 const reader = response.body?.getReader();
68 if (!reader) return;
69
70 while (true) {
71 const { value, done } = await reader.read();
72 if (done) break;
73 console.log("Stream output:", decoder.decode(value));
74 }
75
76 console.log("Stream processing complete.");
77}