Skip to content

Commit 749a7ac

Browse files
authoredMay 16, 2024··
Fix streaming in Node.js fast path (#11058)
* Fix streaming in Node.js fast path * Create a new next if the iterator is not done * Use a flag instead * Update test * Add new assertion * Add explanation of the renderingComplete variable * Remove flaky assertion
1 parent 00420a7 commit 749a7ac

File tree

3 files changed

+22
-7
lines changed

3 files changed

+22
-7
lines changed
 

‎.changeset/fluffy-pears-press.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"astro": patch
3+
---
4+
5+
Fix streaming in Node.js fast path

‎packages/astro/src/runtime/server/render/astro/render.ts

+16-6
Original file line numberDiff line numberDiff line change
@@ -209,14 +209,23 @@ export async function renderToAsyncIterable(
209209
let error: Error | null = null;
210210
// The `next` is an object `{ promise, resolve, reject }` that we use to wait
211211
// for chunks to be pushed into the buffer.
212-
let next = promiseWithResolvers<void>();
212+
let next: ReturnType<typeof promiseWithResolvers<void>> | null = null;
213213
const buffer: Uint8Array[] = []; // []Uint8Array
214+
let renderingComplete = false;
214215

215216
const iterator: AsyncIterator<Uint8Array> = {
216217
async next() {
217218
if (result.cancelled) return { done: true, value: undefined };
218219

219-
await next.promise;
220+
if(next !== null) {
221+
await next.promise;
222+
}
223+
224+
// Only create a new promise if rendering is still ongoing. Otherwise
225+
// there will be a dangling promises that breaks tests (probably not an actual app)
226+
if(!renderingComplete) {
227+
next = promiseWithResolvers();
228+
}
220229

221230
// If an error occurs during rendering, throw the error as we cannot proceed.
222231
if (error) {
@@ -276,8 +285,7 @@ export async function renderToAsyncIterable(
276285
// Push the chunks into the buffer and resolve the promise so that next()
277286
// will run.
278287
buffer.push(bytes);
279-
next.resolve();
280-
next = promiseWithResolvers<void>();
288+
next?.resolve();
281289
}
282290
},
283291
};
@@ -286,12 +294,14 @@ export async function renderToAsyncIterable(
286294
renderPromise
287295
.then(() => {
288296
// Once rendering is complete, calling resolve() allows the iterator to finish running.
289-
next.resolve();
297+
renderingComplete = true;
298+
next?.resolve();
290299
})
291300
.catch((err) => {
292301
// If an error occurs, save it in the scope so that we throw it when next() is called.
293302
error = err;
294-
next.resolve();
303+
renderingComplete = true;
304+
next?.resolve();
295305
});
296306

297307
// This is the Iterator protocol, an object with a `Symbol.asyncIterator`

‎packages/astro/test/streaming.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe('Streaming', () => {
3737
let chunk = decoder.decode(bytes);
3838
chunks.push(chunk);
3939
}
40-
assert.equal(chunks.length > 1, true);
40+
assert.equal(chunks.length > 5, true);
4141
});
4242

4343
it('Body of slots is chunked', async () => {

0 commit comments

Comments
 (0)
Please sign in to comment.