Skip to content

Commit 448929d

Browse files
committed
refactors
1 parent e672f80 commit 448929d

File tree

3 files changed

+21
-34
lines changed

3 files changed

+21
-34
lines changed

.changeset/forty-bikes-tickle.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"ovr": patch
33
---
44

5-
perf: Use a time based strategy to yield back to the event loop instead of iterations for more consistent streaming for sync iterators.
5+
perf: Use a time based strategy to yield back to the event loop instead of iterations for more consistent streaming for sync iterators. Other refactors and minor perf optimizations.

packages/ovr/src/jsx/index.ts

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { IntrinsicElements as IE } from "./intrinsic-elements.js";
44
import { merge } from "./merge-async-generators.js";
55
import { performance } from "node:perf_hooks";
66
import { setImmediate } from "node:timers/promises";
7+
import { types } from "node:util";
78

89
/** ovr JSX namespace */
910
export namespace JSX {
@@ -135,31 +136,20 @@ export async function* toGenerator(
135136

136137
if (typeof element === "object") {
137138
if (Symbol.asyncIterator in element) {
138-
// async iterable - lazily resolve
139+
// any async iterable - lazily resolve
139140
for await (const children of element) yield* toGenerator(children);
140141
return;
141142
}
142143

143144
if (Symbol.iterator in element) {
144145
// sync iterable
145-
const iterator = element[Symbol.iterator]();
146-
147-
if (
148-
typeof iterator.next === "function" &&
149-
typeof iterator.throw === "function" &&
150-
typeof iterator.return === "function"
151-
) {
152-
// sync generator
153-
// process lazily - avoids loading all in memory
146+
if (types.isGeneratorObject(element)) {
147+
// sync generator - lazily resolve, avoids loading all in memory
154148
const ms = 8;
155149
let deadline = performance.now() + ms;
156150

157-
while (true) {
158-
const result = iterator.next();
159-
160-
if (result.done) break;
161-
162-
yield* toGenerator(result.value);
151+
for (const children of element) {
152+
yield* toGenerator(children);
163153

164154
if (performance.now() > deadline) {
165155
// yields back to the event loop if deadline is reached
@@ -174,24 +164,18 @@ export async function* toGenerator(
174164

175165
// other iterable - array, set, etc.
176166
// process children in parallel
177-
const generators: AsyncGenerator<Chunk, void, unknown>[] = [];
178-
179-
while (true) {
180-
const result = iterator.next();
181-
if (result.done) break;
182-
generators.push(toGenerator(result.value));
183-
}
184-
185-
const queue: (Chunk | null)[] = new Array(generators.length);
186-
const complete = new Uint8Array(generators.length);
167+
const generators = Array.from(element, toGenerator);
168+
const n = generators.length;
169+
const queue = new Array<Chunk | null>(n);
170+
const complete = new Uint8Array(n);
187171
let current = 0;
188172

189173
for await (const m of merge(generators)) {
190174
if (m.result.done) {
191175
complete[m.index] = 1;
192176

193177
if (m.index === current) {
194-
while (++current < generators.length) {
178+
while (++current < n) {
195179
if (queue[current]) {
196180
// yield whatever is in the next queue even if it hasn't completed yet
197181
yield queue[current]!;
@@ -212,7 +196,7 @@ export async function* toGenerator(
212196
}
213197

214198
// clear the queue
215-
yield* queue.filter(Boolean) as Chunk[];
199+
yield* queue.filter((chunk) => chunk !== null);
216200
return;
217201
}
218202
}

packages/ovr/src/jsx/merge-async-generators.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ export async function* merge<T>(generators: AsyncGenerator<T, void>[]) {
1616
const iterators = generators.map((gen) => gen[Symbol.asyncIterator]());
1717
const promises = new Map<number, ReturnType<typeof next<T, void>>>();
1818

19-
iterators.forEach((iterator, index) =>
20-
promises.set(index, next(iterator, index)),
21-
);
19+
for (let i = 0; i < iterators.length; i++) {
20+
promises.set(i, next(iterators[i]!, i));
21+
}
2222

2323
let current: Awaited<ReturnType<typeof next>>;
2424

@@ -37,8 +37,11 @@ export async function* merge<T>(generators: AsyncGenerator<T, void>[]) {
3737
}
3838
} finally {
3939
for (const iterator of iterators) {
40-
// catch - could have already returned
41-
iterator.return().catch(() => {});
40+
try {
41+
iterator.return();
42+
} catch {
43+
// could have already returned
44+
}
4245
}
4346
}
4447
}

0 commit comments

Comments
 (0)