Skip to content

Commit

Permalink
Batch iterator buffering to reduce number of timeout calls (#7544)
Browse files Browse the repository at this point in the history
  • Loading branch information
Johannes Spohr authored Jul 4, 2023
1 parent 7419bb6 commit 47b756e
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilled-books-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Batch async iterator buffering to reduce numbers of calls to `setTimeout`
25 changes: 21 additions & 4 deletions packages/astro/src/runtime/server/render/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}

Expand Down

0 comments on commit 47b756e

Please sign in to comment.