Skip to content

Commit 8657e1f

Browse files
committed
perf: Improve UI responsiveness during large sync audits
sync-state.ts: - Reduce chunk size from 5000 to 500 for new record inserts - Add progress logging for large datasets (>5000 records) - Yield after every chunk (not just between chunks) yield.ts: - Use scheduler.yield() when available (Chrome 115+) - Fall back to setTimeout(0) instead of requestAnimationFrame - rAF only fires once per frame (~16ms) which is too slow for data processing; setTimeout(0) allows more frequent yields This should significantly improve UI responsiveness when syncing large datasets (e.g., 25k orders) by breaking work into smaller chunks and yielding more frequently.
1 parent e80a256 commit 8657e1f

File tree

2 files changed

+36
-19
lines changed

2 files changed

+36
-19
lines changed

packages/query/src/sync-state.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,31 @@ export class SyncStateManager {
176176
}));
177177

178178
if (newRecords.length > 0) {
179-
// Process new records in chunks with yielding for very large sets
180-
const newRecordChunkSize = 5000;
179+
// Process new records in small chunks with yielding to keep UI responsive
180+
// Smaller chunks = more yields = smoother UI, but slightly slower overall
181+
// 500 is a good balance for IndexedDB writes
182+
const newRecordChunkSize = 500;
183+
const totalChunks = Math.ceil(newRecords.length / newRecordChunkSize);
184+
181185
for (let i = 0; i < newRecords.length; i += newRecordChunkSize) {
182186
const chunk = newRecords.slice(i, i + newRecordChunkSize);
183187
await this.syncCollection.bulkUpsert(chunk);
184188

185-
// Yield between chunks
186-
if (i + newRecordChunkSize < newRecords.length) {
187-
await yieldToEventLoop();
189+
// Yield after every chunk to keep UI responsive
190+
await yieldToEventLoop();
191+
192+
// Log progress for large sets
193+
if (newRecords.length > 5000) {
194+
const chunkNum = Math.floor(i / newRecordChunkSize) + 1;
195+
if (chunkNum % 10 === 0 || chunkNum === totalChunks) {
196+
syncLogger.debug(`Audit progress: ${chunkNum}/${totalChunks} chunks`, {
197+
context: {
198+
endpoint: this.endpoint,
199+
processed: Math.min(i + newRecordChunkSize, newRecords.length),
200+
total: newRecords.length,
201+
},
202+
});
203+
}
188204
}
189205
}
190206
}

packages/query/src/yield.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
* Yields execution to allow the event loop to process other tasks.
33
*
44
* This prevents UI blocking when processing large datasets.
5-
* Uses requestAnimationFrame when available (browser), falls back to setTimeout.
5+
* Uses the best available yielding mechanism:
6+
* 1. scheduler.yield() - Modern browsers (Chrome 115+), specifically designed for this
7+
* 2. setTimeout(0) - Fallback, allows pending events and renders to process
8+
*
9+
* Note: We don't use requestAnimationFrame because it only fires once per frame
10+
* (~16ms at 60fps), which is too slow for data processing yielding.
611
*
712
* Usage:
813
* ```ts
@@ -13,19 +18,15 @@
1318
* ```
1419
*/
1520
export function yieldToEventLoop(): Promise<void> {
16-
return new Promise((resolve) => {
17-
if (typeof requestAnimationFrame !== 'undefined') {
18-
// Browser environment - yield before next frame
19-
requestAnimationFrame(() => resolve());
20-
} else {
21-
// Node.js or environments without rAF - use setImmediate or setTimeout
22-
if (typeof setImmediate !== 'undefined') {
23-
setImmediate(resolve);
24-
} else {
25-
setTimeout(resolve, 0);
26-
}
27-
}
28-
});
21+
// Use scheduler.yield() if available (modern browsers)
22+
// This is the best option for yielding during data processing
23+
if (typeof scheduler !== 'undefined' && typeof scheduler.yield === 'function') {
24+
return scheduler.yield();
25+
}
26+
27+
// Fallback: setTimeout(0) allows the browser to process events and renders
28+
// This is better than setImmediate (Node.js only) or requestAnimationFrame (too slow)
29+
return new Promise((resolve) => setTimeout(resolve, 0));
2930
}
3031

3132
/**

0 commit comments

Comments
 (0)