fix: Résolution des blocages après l'enregistrement du Service Worker

- Ajout de timeouts sur checkForUpdates() (10s) et waitForServiceWorkerActivation() (15s)
- Gestion d'erreur améliorée pour éviter les blocages infinis
- checkForUpdates() avec timeout de 5s pour éviter les blocages
- waitForServiceWorkerActivation() retourne null au lieu de bloquer
- Gestion d'erreur dans l'intervalle de scan du service worker
- Continuation de l'initialisation même en cas d'échec partiel
- Logs d'avertissement pour diagnostiquer les problèmes
This commit is contained in:
NicolasCantu 2025-10-22 16:07:24 +02:00
parent 17517f861a
commit 937b071100

View File

@ -120,7 +120,7 @@ export class Database {
// No existing workers: register a new one. // No existing workers: register a new one.
this.serviceWorkerRegistration = await navigator.serviceWorker.register(path, { type: 'module' }); this.serviceWorkerRegistration = await navigator.serviceWorker.register(path, { type: 'module' });
console.log('Service Worker registered with scope:', this.serviceWorkerRegistration.scope); console.log('Service Worker registered with scope:', this.serviceWorkerRegistration.scope);
// Show spinner during service worker initialization // Show spinner during service worker initialization
this.showServiceWorkerSpinner('Initializing database service...'); this.showServiceWorkerSpinner('Initializing database service...');
} else if (registrations.length === 1) { } else if (registrations.length === 1) {
@ -135,12 +135,21 @@ export class Database {
console.log('All previous Service Workers unregistered.'); console.log('All previous Service Workers unregistered.');
this.serviceWorkerRegistration = await navigator.serviceWorker.register(path, { type: 'module' }); this.serviceWorkerRegistration = await navigator.serviceWorker.register(path, { type: 'module' });
console.log('Service Worker registered with scope:', this.serviceWorkerRegistration.scope); console.log('Service Worker registered with scope:', this.serviceWorkerRegistration.scope);
// Show spinner during service worker initialization // Show spinner during service worker initialization
this.showServiceWorkerSpinner('Initializing database service...'); this.showServiceWorkerSpinner('Initializing database service...');
} }
await this.checkForUpdates(); // Check for updates with timeout
try {
await Promise.race([
this.checkForUpdates(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Update timeout')), 10000))
]);
} catch (error) {
console.warn('Service worker update failed or timed out:', error);
// Continue anyway - don't block the initialization
}
// Hide spinner once service worker is ready // Hide spinner once service worker is ready
this.hideServiceWorkerSpinner(); this.hideServiceWorkerSpinner();
@ -153,13 +162,20 @@ export class Database {
// Set up a periodic check to ensure the service worker is active and to send a SCAN message. // Set up a periodic check to ensure the service worker is active and to send a SCAN message.
this.serviceWorkerCheckIntervalId = window.setInterval(async () => { this.serviceWorkerCheckIntervalId = window.setInterval(async () => {
const activeWorker = this.serviceWorkerRegistration?.active || (await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration!)); try {
const service = await Services.getInstance(); const activeWorker = this.serviceWorkerRegistration?.active || (await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration!));
const payload = await service.getMyProcesses(); if (activeWorker) {
if (payload && payload.length != 0) { const service = await Services.getInstance();
activeWorker?.postMessage({ type: 'SCAN', payload }); const payload = await service.getMyProcesses();
if (payload && payload.length != 0) {
activeWorker.postMessage({ type: 'SCAN', payload });
}
}
} catch (error) {
console.warn('Service worker scan failed:', error);
// Continue the interval even if one scan fails
} }
}, 5000); }, 5000);
} catch (error) { } catch (error) {
console.error('Service Worker registration failed:', error); console.error('Service Worker registration failed:', error);
} }
@ -167,18 +183,28 @@ export class Database {
// Helper function to wait for service worker activation // Helper function to wait for service worker activation
private async waitForServiceWorkerActivation(registration: ServiceWorkerRegistration): Promise<ServiceWorker | null> { private async waitForServiceWorkerActivation(registration: ServiceWorkerRegistration): Promise<ServiceWorker | null> {
return new Promise((resolve) => { return new Promise((resolve, reject) => {
if (registration.active) { if (registration.active) {
resolve(registration.active); resolve(registration.active);
} else { return;
const listener = () => {
if (registration.active) {
navigator.serviceWorker.removeEventListener('controllerchange', listener);
resolve(registration.active);
}
};
navigator.serviceWorker.addEventListener('controllerchange', listener);
} }
// Set a timeout to prevent infinite waiting
const timeout = setTimeout(() => {
navigator.serviceWorker.removeEventListener('controllerchange', listener);
console.warn('Service worker activation timeout');
resolve(null); // Return null instead of rejecting to allow continuation
}, 15000); // 15 second timeout
const listener = () => {
if (registration.active) {
clearTimeout(timeout);
navigator.serviceWorker.removeEventListener('controllerchange', listener);
resolve(registration.active);
}
};
navigator.serviceWorker.addEventListener('controllerchange', listener);
}); });
} }
@ -186,7 +212,10 @@ export class Database {
if (this.serviceWorkerRegistration) { if (this.serviceWorkerRegistration) {
// Check for updates to the service worker // Check for updates to the service worker
try { try {
await this.serviceWorkerRegistration.update(); await Promise.race([
this.serviceWorkerRegistration.update(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Update timeout')), 5000))
]);
// If there's a new worker waiting, activate it immediately // If there's a new worker waiting, activate it immediately
if (this.serviceWorkerRegistration.waiting) { if (this.serviceWorkerRegistration.waiting) {
@ -194,6 +223,7 @@ export class Database {
} }
} catch (error) { } catch (error) {
console.error('Error checking for service worker updates:', error); console.error('Error checking for service worker updates:', error);
throw error; // Re-throw to be caught by the calling function
} }
} }
} }
@ -228,8 +258,8 @@ export class Database {
// Save data to db // Save data to db
const blob = new Blob([valueBytes], {type: "application/octet-stream"}); const blob = new Blob([valueBytes], {type: "application/octet-stream"});
await service.saveBlobToDb(hash, blob); await service.saveBlobToDb(hash, blob);
document.dispatchEvent(new CustomEvent('newDataReceived', { document.dispatchEvent(new CustomEvent('newDataReceived', {
detail: { detail: {
processId, processId,
stateId, stateId,
hash, hash,
@ -330,7 +360,7 @@ export class Database {
reject(new Error(`Failed to send message to service worker: ${error}`)); reject(new Error(`Failed to send message to service worker: ${error}`));
} }
}); });
} }
public batchWriting(payload: { storeName: string; objects: { key: any; object: any }[] }): Promise<void> { public batchWriting(payload: { storeName: string; objects: { key: any; object: any }[] }): Promise<void> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
@ -462,7 +492,7 @@ export class Database {
private showServiceWorkerSpinner(message: string = 'Initializing...') { private showServiceWorkerSpinner(message: string = 'Initializing...') {
// Remove existing spinner if any // Remove existing spinner if any
this.hideServiceWorkerSpinner(); this.hideServiceWorkerSpinner();
// Create spinner overlay // Create spinner overlay
const overlay = document.createElement('div'); const overlay = document.createElement('div');
overlay.id = 'service-worker-spinner-overlay'; overlay.id = 'service-worker-spinner-overlay';
@ -480,7 +510,7 @@ export class Database {
z-index: 10000; z-index: 10000;
backdrop-filter: blur(5px); backdrop-filter: blur(5px);
`; `;
// Create spinner content // Create spinner content
const spinnerContent = document.createElement('div'); const spinnerContent = document.createElement('div');
spinnerContent.style.cssText = ` spinnerContent.style.cssText = `
@ -493,7 +523,7 @@ export class Database {
max-width: 400px; max-width: 400px;
width: 90%; width: 90%;
`; `;
// Create spinner // Create spinner
const spinner = document.createElement('div'); const spinner = document.createElement('div');
spinner.style.cssText = ` spinner.style.cssText = `
@ -505,7 +535,7 @@ export class Database {
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
margin: 0 auto 15px auto; margin: 0 auto 15px auto;
`; `;
// Create message // Create message
const messageEl = document.createElement('div'); const messageEl = document.createElement('div');
messageEl.textContent = message; messageEl.textContent = message;
@ -515,7 +545,7 @@ export class Database {
font-weight: 500; font-weight: 500;
margin-bottom: 10px; margin-bottom: 10px;
`; `;
// Create progress indicator // Create progress indicator
const progressEl = document.createElement('div'); const progressEl = document.createElement('div');
progressEl.textContent = 'Please wait...'; progressEl.textContent = 'Please wait...';
@ -523,7 +553,7 @@ export class Database {
font-size: 12px; font-size: 12px;
color: #666; color: #666;
`; `;
// Add CSS animation if not already present // Add CSS animation if not already present
if (!document.getElementById('service-worker-spinner-styles')) { if (!document.getElementById('service-worker-spinner-styles')) {
const style = document.createElement('style'); const style = document.createElement('style');
@ -536,13 +566,13 @@ export class Database {
`; `;
document.head.appendChild(style); document.head.appendChild(style);
} }
// Assemble spinner // Assemble spinner
spinnerContent.appendChild(spinner); spinnerContent.appendChild(spinner);
spinnerContent.appendChild(messageEl); spinnerContent.appendChild(messageEl);
spinnerContent.appendChild(progressEl); spinnerContent.appendChild(progressEl);
overlay.appendChild(spinnerContent); overlay.appendChild(spinnerContent);
// Add to document // Add to document
document.body.appendChild(overlay); document.body.appendChild(overlay);
} }