Signatur
Beschreibung
IDBTransaction ist das zentrale Werkzeug für jeden Datenzugriff in IndexedDB: Lesen, Schreiben und Löschen passiert immer innerhalb einer Transaktion. Eine Transaktion erzeugt man mit IDBDatabase.prototype.transaction(storeNames, mode, options). Dabei legt man fest, auf welche Object Stores sie sich bezieht (ihren Scope) und in welchem Modus sie läuft: "readonly", "readwrite" oder "versionchange". Den Modus "versionchange" erzeugt nur der Browser selbst, und zwar im upgradeneeded-Ereignis.
Transaktionen sind atomar. Entweder werden alle Änderungen übernommen (Ereignis complete), oder keine davon (Ereignis abort). Schlägt eine einzelne Anfrage fehl und wird der Fehler nicht mit event.preventDefault() abgefangen, bricht die ganze Transaktion ab. Mit abort() macht man alle Änderungen ausdrücklich rückgängig. commit() schließt die Transaktion dagegen vorzeitig ab, ohne auf das automatische Ende zu warten.
Wichtige Eigenschaften und Methoden:
db: die zugehörigeIDBDatabasemode:"readonly","readwrite"oder"versionchange"objectStoreNames: eineDOMStringListmit den Stores im Scopedurability:"default","strict"oder"relaxed"error: eineDOMException, wenn die Transaktion abgebrochen wurdeobjectStore(name): liefert einenIDBObjectStoreaus dem Scopeabort()undcommit()- Ereignisse:
complete,abortunderror, jeweils peraddEventListeneroder überoncomplete,onabortundonerror
Eine Transaktion bleibt nur so lange aktiv, wie in ihren Callbacks neue Anfragen gestellt werden. Gibt man die Kontrolle an die Ereignisschleife zurück, ohne eine offene Anfrage zu haben (etwa durch ein await auf fetch()), wird sie automatisch abgeschlossen. Danach wirft jeder weitere Zugriff einen TransactionInactiveError.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $storeNames Pflicht | string | string[] | Parameter von IDBDatabase.transaction(): Name oder Liste der Object Stores, auf die die Transaktion zugreifen darf. |
|
| $mode | "readonly" | "readwrite" | "readonly" | Zugriffsmodus. Mehrere readonly-Transaktionen dürfen parallel laufen. readwrite-Transaktionen mit überlappendem Scope laufen nacheinander. |
| $options | { durability?: "default" | "strict" | "relaxed" } | { durability: "default" } | Mit "relaxed" meldet der Browser complete schneller, ohne vorher auf den Plattenschreibvorgang zu warten. "strict" erzwingt das Schreiben auf die Platte vor complete. |
Rückgabewert
IDBDatabase.prototype.transaction() gibt ein neues, aktives IDBTransaction-Objekt zurück.Beispiele
Datensätze in einer readwrite-Transaktion speichern
const openDb = () => new Promise((resolve, reject) => {
const req = indexedDB.open('shop', 1);
req.onupgradeneeded = () => {
req.result.createObjectStore('produkte', { keyPath: 'id' });
};
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
const db = await openDb();
const tx = db.transaction('produkte', 'readwrite');
const store = tx.objectStore('produkte');
store.put({ id: 1, name: 'Tastatur', preis: 49.9 });
store.put({ id: 2, name: 'Maus', preis: 19.9 });
tx.addEventListener('complete', () => console.log('Gespeichert, Modus:', tx.mode));
tx.addEventListener('error', () => console.error('Fehler:', tx.error));
Transaktion als Promise verpacken
const txDone = (tx) => new Promise((resolve, reject) => {
tx.oncomplete = () => resolve();
tx.onabort = () => reject(tx.error ?? new DOMException('Abgebrochen', 'AbortError'));
tx.onerror = () => reject(tx.error);
});
const db = await new Promise((res, rej) => {
const r = indexedDB.open('shop', 1);
r.onupgradeneeded = () => r.result.createObjectStore('produkte', { keyPath: 'id' });
r.onsuccess = () => res(r.result);
r.onerror = () => rej(r.error);
});
const tx = db.transaction(['produkte'], 'readwrite', { durability: 'relaxed' });
tx.objectStore('produkte').delete(2);
await txDone(tx);
console.log('Löschen abgeschlossen');
Änderungen bei Validierungsfehler mit abort() verwerfen
const db = await new Promise((res, rej) => {
const r = indexedDB.open('shop', 1);
r.onupgradeneeded = () => r.result.createObjectStore('produkte', { keyPath: 'id' });
r.onsuccess = () => res(r.result);
r.onerror = () => rej(r.error);
});
const importiere = (produkte) => {
const tx = db.transaction('produkte', 'readwrite');
const store = tx.objectStore('produkte');
tx.onabort = () => console.log('Import verworfen:', tx.error?.name ?? 'manuell');
tx.oncomplete = () => console.log('Import erfolgreich');
for (const p of produkte) {
if (typeof p.preis !== 'number') {
tx.abort(); // alle bisherigen put()-Aufrufe werden zurückgenommen
return;
}
store.put(p);
}
tx.commit(); // sofort abschließen, nicht auf das automatische Ende warten
};
importiere([{ id: 3, name: 'Monitor', preis: 199 }, { id: 4, name: 'Kabel', preis: 'gratis' }]);
// Wichtig · Fallstricke
- Automatisches Abschließen: Zwischen zwei Anfragen darf man nicht auf Promises warten, die nichts mit IndexedDB zu tun haben (z. B.
await fetch()odersetTimeout). Die Transaktion schließt sonst automatisch ab, und die nächste Anfrage wirft einenTransactionInactiveError. Externe Daten deshalb holen, bevor man die Transaktion öffnet. successheißt noch nicht gespeichert: Dassuccess-Ereignis einer einzelnenIDBRequestbedeutet nicht, dass die Daten dauerhaft geschrieben sind. Darauf verlassen kann man sich erst nach demcomplete-Ereignis der Transaktion.- Fehler blubbern hoch: Das
error-Ereignis einer Anfrage steigt zur Transaktion und danach zur Datenbank auf. OhnepreventDefault()führt es zum Abbruch (abort). - Scope: Wer
objectStore()mit einem Store außerhalb des Scopes aufruft, bekommt einenNotFoundError. Wer in einerreadonly-Transaktion schreiben will, bekommt einenReadOnlyError. - Performance:
readwrite-Transaktionen mit überlappendem Scope sperren sich gegenseitig. Den Scope daher so klein wie möglich halten und viele Schreibvorgänge in einer Transaktion bündeln, statt für jeden eine eigene zu öffnen. - Kompatibilität: IndexedDB selbst läuft in allen modernen Browsern und in Web Workern.
commit()unddurabilitygibt es erst in neueren Versionen (Chrome 76+/83+, Firefox 74+/126+, Safari 15+). Bei Bedarf vorher mit'commit' in IDBTransaction.prototypeprüfen. Im privaten Modus mancher Browser ist der Speicher eingeschränkt oder wird beim Schließen gelöscht. - Die früheren numerischen Konstanten (
IDBTransaction.READ_ONLYusw.) sind veraltet. Stattdessen die String-Werte verwenden.