Signatur
Beschreibung
Die schreibgeschützte Eigenschaft indexedDB der Schnittstelle WorkerGlobalScope liefert ein IDBFactory-Objekt. Damit können Dedicated Worker, Shared Worker und Service Worker IndexedDB-Datenbanken öffnen, anlegen und löschen, und zwar genauso wie im Hauptthread über window.indexedDB.
Im Worker-Kontext ist indexedDB global verfügbar. Man kann also direkt indexedDB.open() oder self.indexedDB.open() schreiben. Das eignet sich gut, um große Datenmengen außerhalb des Hauptthreads zu speichern oder zu verarbeiten, zum Beispiel beim Importieren großer JSON-Dateien, beim Zwischenspeichern von API-Antworten oder für Offline-Daten in einem Service Worker. Die Benutzeroberfläche bleibt dabei flüssig.
Worker und Hauptthread mit demselben Ursprung (Origin) greifen auf dieselben Datenbanken zu. Daten, die ein Worker schreibt, kann die Seite also direkt lesen. Die API arbeitet ereignisbasiert mit IDBRequest und den Ereignissen success, error und upgradeneeded. In der Praxis verpackt man sie deshalb meist in Promises.
Rückgabewert
IDBFactory-Objekt mit den Methoden open(), deleteDatabase(), databases() und cmp().Beispiele
Datenbank im Worker öffnen und Object Store anlegen
// worker.js
const request = indexedDB.open('app-cache', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('artikel')) {
db.createObjectStore('artikel', { keyPath: 'id' });
}
};
request.onsuccess = () => {
const db = request.result;
console.log('Datenbank geöffnet:', db.name, 'Version', db.version);
postMessage({ status: 'bereit' });
};
request.onerror = () => {
console.error('Fehler beim Öffnen:', request.error);
};
Große Datenmengen im Worker per Promise-Wrapper speichern
// worker.js
const openDb = () => new Promise((resolve, reject) => {
const req = self.indexedDB.open('app-cache', 1);
req.onupgradeneeded = () => req.result.createObjectStore('artikel', { keyPath: 'id' });
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
self.onmessage = async ({ data }) => {
const response = await fetch(data.url);
const artikel = await response.json();
const db = await openDb();
const tx = db.transaction('artikel', 'readwrite');
const store = tx.objectStore('artikel');
artikel.forEach((a) => store.put(a));
tx.oncomplete = () => postMessage({ gespeichert: artikel.length });
tx.onerror = () => postMessage({ fehler: tx.error?.message });
};
// main.js
// const worker = new Worker('worker.js');
// worker.onmessage = (e) => console.log(e.data);
// worker.postMessage({ url: '/api/artikel' });
Service Worker: Offline-Daten aus IndexedDB ausliefern
// sw.js
const getAll = (storeName) => new Promise((resolve, reject) => {
const req = indexedDB.open('app-cache', 1);
req.onsuccess = () => {
const tx = req.result.transaction(storeName, 'readonly');
const getReq = tx.objectStore(storeName).getAll();
getReq.onsuccess = () => resolve(getReq.result);
getReq.onerror = () => reject(getReq.error);
};
req.onerror = () => reject(req.error);
});
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
if (url.pathname === '/api/artikel') {
event.respondWith(
fetch(event.request).catch(async () => {
const daten = await getAll('artikel');
return new Response(JSON.stringify(daten), {
headers: { 'Content-Type': 'application/json' }
});
})
);
}
});
Vorhandene Datenbanken auflisten
// worker.js
const listen = async () => {
if ('databases' in indexedDB) {
const dbs = await indexedDB.databases();
dbs.forEach(({ name, version }) => console.log(`${name} (v${version})`));
} else {
console.log('indexedDB.databases() wird nicht unterstützt');
}
};
listen();
// Wichtig · Fallstricke
- Kein synchroner Zugriff: Die früher geplante synchrone IndexedDB-API für Worker wurde nie umgesetzt. Auch im Worker ist alles asynchron.
- Kein
localStoragein Workern: In Workern gibt es keinlocalStorageund keinsessionStorage. IndexedDB (oder die Cache API bzw. OPFS) ist dort der Weg, Daten dauerhaft zu speichern. - Transaktionen schließen sich automatisch: Eine Transaktion wird beendet, sobald keine Anfragen mehr ausstehen. Wer zwischen zwei Operationen
await fetch()aufruft, bekommt einenTransactionInactiveError. Daher erst alle Daten laden und danach die Transaktion öffnen. - Versionskonflikte: Öffnen Seite und Worker dieselbe Datenbank mit unterschiedlichen Versionen, blockiert das Upgrade (Ereignis
blocked). Deshalb aufversionchangereagieren und die Verbindung mitdb.close()schließen. - Privater Modus und Speicherkontingente: In manchen Browsern ist IndexedDB im privaten Modus eingeschränkt oder wird beim Schließen gelöscht. Wenn das Kontingent überschritten ist, tritt ein
QuotaExceededErrorauf. - Ursprung: Worker aus
blob:- oderdata:-URLs können einen undurchsichtigen Ursprung haben. Dann löst der Zugriff einenSecurityErroraus. - Kompatibilität: Wird in allen modernen Browsern unterstützt.
indexedDB.databases()ist in Firefox erst seit Version 126 verfügbar, deshalb vorher per Feature-Erkennung prüfen.