summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/olive-gorillas-cheat.md5
-rw-r--r--packages/astro/src/runtime/server/render/any.ts6
-rw-r--r--packages/astro/src/runtime/server/render/astro/render-template.ts18
-rw-r--r--packages/astro/src/runtime/server/render/util.ts17
-rw-r--r--packages/astro/test/fixtures/parallel/src/pages/index.astro3
5 files changed, 33 insertions, 16 deletions
diff --git a/.changeset/olive-gorillas-cheat.md b/.changeset/olive-gorillas-cheat.md
new file mode 100644
index 000000000..0c20121ab
--- /dev/null
+++ b/.changeset/olive-gorillas-cheat.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Render arrays of components in parallel
diff --git a/packages/astro/src/runtime/server/render/any.ts b/packages/astro/src/runtime/server/render/any.ts
index cded2c496..4ee947ee6 100644
--- a/packages/astro/src/runtime/server/render/any.ts
+++ b/packages/astro/src/runtime/server/render/any.ts
@@ -5,6 +5,7 @@ import {
renderAstroTemplateResult,
} from './astro/index.js';
import { SlotString } from './slot.js';
+import { bufferIterators } from './util.js';
export async function* renderChild(child: any): AsyncIterable<any> {
child = await child;
@@ -16,8 +17,9 @@ export async function* renderChild(child: any): AsyncIterable<any> {
} else if (isHTMLString(child)) {
yield child;
} else if (Array.isArray(child)) {
- for (const value of child) {
- yield markHTMLString(await renderChild(value));
+ const bufferedIterators = bufferIterators(child.map((c) => renderChild(c)));
+ for (const value of bufferedIterators) {
+ yield markHTMLString(await value);
}
} else if (typeof child === 'function') {
// Special: If a child is a function, call it automatically.
diff --git a/packages/astro/src/runtime/server/render/astro/render-template.ts b/packages/astro/src/runtime/server/render/astro/render-template.ts
index 2433596bd..b0dbabdc1 100644
--- a/packages/astro/src/runtime/server/render/astro/render-template.ts
+++ b/packages/astro/src/runtime/server/render/astro/render-template.ts
@@ -3,7 +3,7 @@ import type { RenderInstruction } from '../types';
import { HTMLBytes, markHTMLString } from '../../escape.js';
import { isPromise } from '../../util.js';
import { renderChild } from '../any.js';
-import { EagerAsyncIterableIterator } from '../util.js';
+import { bufferIterators } from '../util.js';
const renderTemplateResultSym = Symbol.for('astro.renderTemplateResult');
@@ -36,23 +36,15 @@ export class RenderTemplateResult {
async *[Symbol.asyncIterator]() {
const { htmlParts, expressions } = this;
- let iterables: Array<EagerAsyncIterableIterator> = [];
- // all async iterators start running in non-buffered mode to avoid useless caching
- for (let i = 0; i < htmlParts.length; i++) {
- iterables.push(new EagerAsyncIterableIterator(renderChild(expressions[i])));
- }
- // 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
- setTimeout(() => {
- // buffer all iterators that haven't started yet
- iterables.forEach((it) => !it.isStarted() && it.buffer());
- }, 0);
+ let iterables = bufferIterators(expressions.map((e) => renderChild(e)));
for (let i = 0; i < htmlParts.length; i++) {
const html = htmlParts[i];
const iterable = iterables[i];
yield markHTMLString(html);
- yield* iterable;
+ if (iterable) {
+ yield* iterable;
+ }
}
}
}
diff --git a/packages/astro/src/runtime/server/render/util.ts b/packages/astro/src/runtime/server/render/util.ts
index c2599401d..1f0aae047 100644
--- a/packages/astro/src/runtime/server/render/util.ts
+++ b/packages/astro/src/runtime/server/render/util.ts
@@ -139,6 +139,23 @@ export function renderElement(
return `<${name}${internalSpreadAttributes(props, shouldEscape)}>${children}</${name}>`;
}
+/**
+ * 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
+ * first sync iterables won't be buffered.
+ */
+export function bufferIterators<T>(iterators: AsyncIterable<T>[]): AsyncIterable<T>[] {
+ // all async iterators start running in non-buffered mode to avoid useless caching
+ 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
+ setTimeout(() => {
+ // buffer all iterators that haven't started yet
+ eagerIterators.forEach((it) => !it.isStarted() && it.buffer());
+ }, 0);
+ return eagerIterators;
+}
+
// This wrapper around an AsyncIterable can eagerly consume its values, so that
// its values are ready to yield out ASAP. This is used for list-like usage of
// Astro components, so that we don't have to wait on earlier components to run
diff --git a/packages/astro/test/fixtures/parallel/src/pages/index.astro b/packages/astro/test/fixtures/parallel/src/pages/index.astro
index 952e9efce..b0571103e 100644
--- a/packages/astro/test/fixtures/parallel/src/pages/index.astro
+++ b/packages/astro/test/fixtures/parallel/src/pages/index.astro
@@ -1,8 +1,9 @@
---
-import Delayed from '../components/Delayed.astro'
+import Delayed from '../components/Delayed.astro';
---
<Delayed ms={30} />
<Delayed ms={20} />
<Delayed ms={40} />
<Delayed ms={10} />
+{[<Delayed ms={30} />, <Delayed ms={20} />, <Delayed ms={40} />, <Delayed ms={10} />]}