summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/chilled-books-shave.md5
-rw-r--r--packages/astro/src/runtime/server/render/util.ts25
2 files changed, 26 insertions, 4 deletions
diff --git a/.changeset/chilled-books-shave.md b/.changeset/chilled-books-shave.md
new file mode 100644
index 000000000..bc1e0c6ee
--- /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 817ca5c32..f422f66d5 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}</${name}>`;
}
+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<T>(iterators: AsyncIterable<T>[]): 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;
}