Spaces:
Running
Running
async function loadFragments() { | |
// Find all elements with ids starting with 'fragment-' | |
const fragmentElements = Array.from(document.querySelectorAll('[id^="fragment-"]')); | |
class FetchQueue { | |
constructor(maxConcurrent = 3) { | |
this.queue = []; | |
this.maxConcurrent = maxConcurrent; | |
this.activeFetches = 0; | |
this.maxRetries = 3; // Maximum number of retry attempts | |
this.baseDelay = 1000; // Base delay in milliseconds (1 second) | |
} | |
async sleep(ms) { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
} | |
async fetchWithRetry(fragmentPath, retryCount = 0) { | |
try { | |
const response = await fetch(fragmentPath); | |
if (!response.ok) { | |
throw new Error(`HTTP error! status: ${response.status}`); | |
} | |
return await response.text(); | |
} catch (error) { | |
if (retryCount < this.maxRetries) { | |
// Exponential backoff: 1s, 2s, 4s | |
const delay = this.baseDelay * Math.pow(2, retryCount); | |
console.warn(`Retry ${retryCount + 1}/${this.maxRetries} for ${fragmentPath} after ${delay}ms`); | |
await this.sleep(delay); | |
return this.fetchWithRetry(fragmentPath, retryCount + 1); | |
} | |
throw error; | |
} | |
} | |
async addFetch(element) { | |
const fragmentName = element.id.replace('fragment-', ''); | |
const fragmentPath = `fragments/${fragmentName}.html`; | |
return new Promise(async (resolve, reject) => { | |
try { | |
const fetchPromise = (async () => { | |
try { | |
const html = await this.fetchWithRetry(fragmentPath); | |
// Process the fragment | |
const temp = document.createElement('div'); | |
temp.innerHTML = html; | |
element.innerHTML = temp.innerHTML; | |
// Handle scripts | |
const scripts = temp.getElementsByTagName('script'); | |
Array.from(scripts).forEach(oldScript => { | |
const newScript = document.createElement('script'); | |
Array.from(oldScript.attributes).forEach(attr => { | |
newScript.setAttribute(attr.name, attr.value); | |
}); | |
newScript.textContent = oldScript.textContent; | |
oldScript.parentNode.removeChild(oldScript); | |
document.body.appendChild(newScript); | |
}); | |
this.activeFetches--; | |
resolve(); | |
} catch (error) { | |
console.error(`Failed to load fragment ${fragmentPath} after ${this.maxRetries} retries:`, error); | |
this.activeFetches--; | |
reject(error); | |
} | |
})(); | |
this.queue.push(fetchPromise); | |
this.activeFetches++; | |
} catch (error) { | |
reject(error); | |
} | |
}); | |
} | |
async processNext(element) { | |
if (this.activeFetches < this.maxConcurrent && element) { | |
await this.addFetch(element); | |
} | |
} | |
} | |
// Initialize queue | |
const fetchQueue = new FetchQueue(3); | |
let currentIndex = 0; | |
const elements = fragmentElements; // Assuming this is defined elsewhere | |
// Initial loading of first 3 elements | |
while (currentIndex < elements.length && currentIndex < 3) { | |
await fetchQueue.processNext(elements[currentIndex]); | |
currentIndex++; | |
} | |
// Process remaining elements as fetches complete | |
while (currentIndex < elements.length) { | |
// Wait for any fetch to complete | |
await Promise.race(fetchQueue.queue); | |
// Remove completed fetch from queue | |
fetchQueue.queue = fetchQueue.queue.filter(p => p.status === 'pending'); | |
// Add next element to queue | |
await fetchQueue.processNext(elements[currentIndex]); | |
currentIndex++; | |
} | |
// Wait for remaining fetches to complete | |
await Promise.all(fetchQueue.queue); | |
} | |
export { loadFragments } |