refactor: extract #processFont and #scheduleProcessing from touch and #processQueue

This commit is contained in:
Ilia Mashkov
2026-04-04 09:52:45 +03:00
parent f88729cc77
commit 20110168f2

View File

@@ -131,26 +131,31 @@ export class AppliedFontsManager {
hasNewItems = true;
}
// Schedule queue processing if we have new items and no existing timer
if (hasNewItems && !this.#timeoutId) {
// Prefer requestIdleCallback for better performance (waits for browser idle)
if (typeof requestIdleCallback !== 'undefined') {
this.#timeoutId = requestIdleCallback(
() => this.#processQueue(),
{ timeout: 150 },
) as unknown as ReturnType<typeof setTimeout>;
this.#pendingType = 'idle';
} else {
// Fallback to setTimeout with ~60fps timing
this.#timeoutId = setTimeout(() => this.#processQueue(), 16);
this.#pendingType = 'timeout';
}
this.#scheduleProcessing();
}
} catch (error) {
console.error(error);
}
}
/**
* Schedules `#processQueue()` via `requestIdleCallback` (150ms timeout) when available,
* falling back to `setTimeout(16ms)` for ~60fps timing.
*/
#scheduleProcessing(): void {
if (typeof requestIdleCallback !== 'undefined') {
this.#timeoutId = requestIdleCallback(
() => this.#processQueue(),
{ timeout: 150 },
) as unknown as ReturnType<typeof setTimeout>;
this.#pendingType = 'idle';
} else {
this.#timeoutId = setTimeout(() => this.#processQueue(), 16);
this.#pendingType = 'timeout';
}
}
/** Returns true if data-saver mode is enabled (defers non-critical weights). */
#shouldDeferNonCritical(): boolean {
return (navigator as any).connection?.saveData === true;
@@ -229,19 +234,7 @@ export class AppliedFontsManager {
continue;
}
try {
// Parse buffer into FontFace and register with document
const font = await loadFont(config, buffer);
this.#loadedFonts.set(key, font);
this.#urlByKey.set(key, config.url);
this.statuses.set(key, 'loaded');
} catch (e) {
if (e instanceof FontParseError) {
console.error(`Font parse failed: ${config.name}`, e);
this.statuses.set(key, 'error');
this.#queue.incrementRetry(key);
}
}
await this.#processFont(key, config, buffer);
// Yield to main thread if needed (prevents UI blocking)
// Chromium: use isInputPending() for optimal responsiveness
@@ -257,6 +250,25 @@ export class AppliedFontsManager {
}
}
/**
* Parses a fetched buffer into a {@link FontFace}, registers it with `document.fonts`,
* and updates reactive status. On failure, sets status to `'error'` and increments the retry count.
*/
async #processFont(key: string, config: FontLoadRequestConfig, buffer: ArrayBuffer): Promise<void> {
try {
const font = await loadFont(config, buffer);
this.#loadedFonts.set(key, font);
this.#urlByKey.set(key, config.url);
this.statuses.set(key, 'loaded');
} catch (e) {
if (e instanceof FontParseError) {
console.error(`Font parse failed: ${config.name}`, e);
this.statuses.set(key, 'error');
this.#queue.incrementRetry(key);
}
}
}
/** Removes fonts unused within TTL (LRU-style cleanup). Runs every PURGE_INTERVAL. Pinned fonts are never evicted. */
#purgeUnused() {
const now = Date.now();