refactor: extract #fetchChunk, replace Promise.allSettled with self-describing results
This commit is contained in:
@@ -191,34 +191,7 @@ export class AppliedFontsManager {
|
|||||||
// ==================== PHASE 1: Concurrent Fetching ====================
|
// ==================== PHASE 1: Concurrent Fetching ====================
|
||||||
// Fetch multiple font files in parallel since network I/O is non-blocking
|
// Fetch multiple font files in parallel since network I/O is non-blocking
|
||||||
for (let i = 0; i < entries.length; i += concurrency) {
|
for (let i = 0; i < entries.length; i += concurrency) {
|
||||||
// Process in chunks based on concurrency limit
|
await this.#fetchChunk(entries.slice(i, i + concurrency), buffers);
|
||||||
const chunk = entries.slice(i, i + concurrency);
|
|
||||||
const results = await Promise.allSettled(
|
|
||||||
chunk.map(async ([key, config]) => {
|
|
||||||
this.statuses.set(key, 'loading');
|
|
||||||
// Fetch buffer via cache (checks memory → Cache API → network)
|
|
||||||
const buffer = await this.#cache.get(config.url, this.#abortController.signal);
|
|
||||||
buffers.set(key, buffer);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle fetch errors - set status and increment retry count
|
|
||||||
for (let j = 0; j < results.length; j++) {
|
|
||||||
if (results[j].status === 'rejected') {
|
|
||||||
const [key, config] = chunk[j];
|
|
||||||
const reason = (results[j] as PromiseRejectedResult).reason;
|
|
||||||
// Aborted fetches are not retriable failures — skip silently
|
|
||||||
const isAbort = reason instanceof FontFetchError
|
|
||||||
&& reason.cause instanceof Error
|
|
||||||
&& reason.cause.name === 'AbortError';
|
|
||||||
if (isAbort) continue;
|
|
||||||
if (reason instanceof FontFetchError) {
|
|
||||||
console.error(`Font fetch failed: ${config.name}`, reason);
|
|
||||||
}
|
|
||||||
this.statuses.set(key, 'error');
|
|
||||||
this.#queue.incrementRetry(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== PHASE 2: Sequential Parsing ====================
|
// ==================== PHASE 2: Sequential Parsing ====================
|
||||||
@@ -250,6 +223,43 @@ export class AppliedFontsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a chunk of fonts concurrently and populates `buffers` with successful results.
|
||||||
|
* Each promise carries its own key and config so results need no index correlation.
|
||||||
|
* Aborted fetches are silently skipped; other errors set status to `'error'` and increment retry.
|
||||||
|
*/
|
||||||
|
async #fetchChunk(
|
||||||
|
chunk: Array<[string, FontLoadRequestConfig]>,
|
||||||
|
buffers: Map<string, ArrayBuffer>,
|
||||||
|
): Promise<void> {
|
||||||
|
const results = await Promise.all(
|
||||||
|
chunk.map(async ([key, config]) => {
|
||||||
|
this.statuses.set(key, 'loading');
|
||||||
|
try {
|
||||||
|
const buffer = await this.#cache.get(config.url, this.#abortController.signal);
|
||||||
|
buffers.set(key, buffer);
|
||||||
|
return { ok: true as const, key };
|
||||||
|
} catch (reason) {
|
||||||
|
return { ok: false as const, key, config, reason };
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const result of results) {
|
||||||
|
if (result.ok) continue;
|
||||||
|
const { key, config, reason } = result;
|
||||||
|
const isAbort = reason instanceof FontFetchError
|
||||||
|
&& reason.cause instanceof Error
|
||||||
|
&& reason.cause.name === 'AbortError';
|
||||||
|
if (isAbort) continue;
|
||||||
|
if (reason instanceof FontFetchError) {
|
||||||
|
console.error(`Font fetch failed: ${config.name}`, reason);
|
||||||
|
}
|
||||||
|
this.statuses.set(key, 'error');
|
||||||
|
this.#queue.incrementRetry(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a fetched buffer into a {@link FontFace}, registers it with `document.fonts`,
|
* 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.
|
* and updates reactive status. On failure, sets status to `'error'` and increments the retry count.
|
||||||
|
|||||||
Reference in New Issue
Block a user