返回 DeepSeek-Reasonix
rafBatch.ts
根目录 / desktop / frontend / src / lib / rafBatch.ts
1 // Coalesces text/reasoning stream deltas into one flush per animation frame.
2 // Non-text events must drain() first so causal ordering is preserved.
3
4 type Flush<T> = (batch: T[]) => void;
5
6 interface BatchHandle<T> {
7 push: (item: T) => void;
8 drain: () => void;
9 size: () => number;
10 }
11
12 export function createRafBatch<T>(flush: Flush<T>): BatchHandle<T> {
13 let buffer: T[] = [];
14 let scheduled: number | null = null;
15
16 const run = () => {
17 scheduled = null;
18 // Snapshot + clear before flushing so a re-entrant push() lands next frame.
19 const out = buffer;
20 buffer = [];
21 if (out.length > 0) flush(out);
22 };
23
24 const handle: BatchHandle<T> = {
25 push(item: T) {
26 buffer.push(item);
27 if (scheduled === null && typeof requestAnimationFrame !== "undefined") {
28 scheduled = requestAnimationFrame(run);
29 } else if (scheduled === null) {
30 // No rAF (SSR / JSDOM) — fall back to a microtask.
31 scheduled = 1;
32 Promise.resolve().then(run);
33 }
34 },
35 drain() {
36 if (scheduled !== null) {
37 if (typeof cancelAnimationFrame !== "undefined" && scheduled !== 1) {
38 cancelAnimationFrame(scheduled);
39 }
40 scheduled = null;
41 }
42 run();
43 },
44 size() {
45 return buffer.length;
46 },
47 };
48 return handle;
49 }
50
50 lines TYPESCRIPT