diff --git a/.changeset/chilled-books-shave.md b/.changeset/chilled-books-shave.md new file mode 100644 index 0000000000..bc1e0c6eec --- /dev/null +++ b/.changeset/chilled-books-shave.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Batch async iterator buffering to reduce numbers of calls to `setTimeout` diff --git a/packages/astro/src/runtime/server/render/util.ts b/packages/astro/src/runtime/server/render/util.ts index 817ca5c324..f422f66d57 100644 --- a/packages/astro/src/runtime/server/render/util.ts +++ b/packages/astro/src/runtime/server/render/util.ts @@ -146,6 +146,26 @@ export function renderElement( return `<${name}${internalSpreadAttributes(props, shouldEscape)}>${children}`; } +const iteratorQueue: EagerAsyncIterableIterator[][] = []; + +/** + * Takes an array of iterators and adds them to a list of iterators to start buffering + * as soon as the execution flow is suspended for the first time. We expect a lot + * of calls to this function before the first suspension, so to reduce the number + * of calls to setTimeout we batch the buffering calls. + * @param iterators + */ +function queueIteratorBuffers(iterators: EagerAsyncIterableIterator[]) { + if (iteratorQueue.length === 0) { + setTimeout(() => { + // buffer all iterators that haven't started yet + iteratorQueue.forEach((its) => its.forEach((it) => !it.isStarted() && it.buffer())); + iteratorQueue.length = 0; // fastest way to empty an array + }); + } + iteratorQueue.push(iterators); +} + /** * This will take an array of async iterables and start buffering them eagerly. * To avoid useless buffering, it will only start buffering the next tick, so the @@ -156,10 +176,7 @@ export function bufferIterators(iterators: AsyncIterable[]): AsyncIterable const eagerIterators = iterators.map((it) => new EagerAsyncIterableIterator(it)); // once the execution of the next for loop is suspended due to an async component, // this timeout triggers and we start buffering the other iterators - queueMicrotask(() => { - // buffer all iterators that haven't started yet - eagerIterators.forEach((it) => !it.isStarted() && it.buffer()); - }); + queueIteratorBuffers(eagerIterators); return eagerIterators; }