Signatur
Beschreibung
LockManager ist der Einstiegspunkt der Web Locks API. Eine Instanz erzeugt man nicht selbst. Man erreicht sie über navigator.locks im Fenster-Kontext oder in Workern (self.navigator.locks). Mit der Schnittstelle stimmen sich mehrere Tabs, iframes und Worker desselben Ursprungs (Origin) ab, damit nur einer von ihnen gleichzeitig auf eine gemeinsame Ressource zugreift. Typische Ressourcen sind eine IndexedDB-Synchronisation, ein Token-Refresh oder eine WebSocket-Verbindung, die nur einmal existieren soll.
Die Schnittstelle hat zwei Methoden:
request(name, [options], callback)fordert eine Sperre mit einem frei gewählten Namen an. Sobald die Sperre erteilt ist, wirdcallbackmit einemLock-Objekt aufgerufen. Die Sperre bleibt bestehen, bis der Callback fertig ist. Gibt der Callback ein Promise zurück, wird die Sperre erst freigegeben, wenn dieses Promise erfüllt oder abgelehnt ist.query()liefert eine Momentaufnahme: welche Sperren gerade gehalten werden (held) und welche noch warten (pending). Das ist vor allem für Fehlersuche und Monitoring nützlich.
Es gibt zwei Sperrmodi: "exclusive" (Standard, immer nur ein Halter) und "shared" (beliebig viele gleichzeitige Halter, z. B. für Lesezugriffe). Solange eine exklusive Sperre gehalten wird, erhält niemand eine geteilte Sperre, und umgekehrt. Anfragen werden der Reihe nach abgearbeitet, in der sie eingegangen sind.
Die Parameter unten beziehen sich auf die zentrale Methode LockManager.prototype.request().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $name Pflicht | string | Frei wählbarer Name der Sperre, gültig innerhalb des Ursprungs. Namen, die mit - beginnen, sind reserviert und lösen einen NotSupportedError aus. |
|
| $options | object | {} | Optionales Konfigurationsobjekt mit den unten aufgeführten Feldern. |
| $options.mode | "exclusive" | "shared" | "exclusive" | Sperrmodus: exklusiv (ein Halter) oder geteilt (mehrere Halter gleichzeitig). |
| $options.ifAvailable | boolean | false | Bei true wird die Sperre nur erteilt, wenn sie sofort frei ist. Andernfalls wird der Callback sofort mit null aufgerufen, statt zu warten. |
| $options.steal | boolean | false | Bei true werden bestehende Sperren gleichen Namens freigegeben und die Anfrage sofort erfüllt. Die Promises der bisherigen Halter werden mit AbortError abgelehnt. Nur zusammen mit mode: "exclusive" erlaubt, nicht mit ifAvailable oder signal. |
| $options.signal | AbortSignal | Damit lässt sich eine noch wartende Anfrage abbrechen, etwa per Zeitlimit. Das Promise wird dann mit AbortError abgelehnt. Eine bereits erteilte Sperre wird dadurch nicht freigegeben. |
|
| $callback Pflicht | (lock: Lock | null) => any | Promise<any> | Wird aufgerufen, sobald die Sperre erteilt ist (bzw. mit null bei ifAvailable, wenn die Sperre belegt ist). Die Sperre gilt, bis der Callback bzw. sein zurückgegebenes Promise abgeschlossen ist. |
Rückgabewert
request() gibt ein Promise zurück. Es wird mit dem Rückgabewert des Callbacks erfüllt, sobald die Sperre wieder freigegeben ist. query() liefert ein Promise auf ein Objekt { held: LockInfo[], pending: LockInfo[] }. Jedes LockInfo enthält name, mode und clientId.Beispiele
Exklusive Sperre für einen Token-Refresh über mehrere Tabs
const refreshToken = async () => {
// Nur ein Tab gleichzeitig darf den Token erneuern
return navigator.locks.request('auth-refresh', async (lock) => {
console.log(`Sperre erhalten: ${lock.name} (${lock.mode})`);
const res = await fetch('/api/token/refresh', { method: 'POST' });
const { token } = await res.json();
localStorage.setItem('token', token);
return token; // wird zum Wert des äußeren Promise
});
};
const token = await refreshToken();
console.log('Neuer Token gespeichert');
Nur ausführen, wenn frei (ifAvailable) – z. B. ein einziger Sync-Leader
const startSyncIfLeader = () =>
navigator.locks.request('sync-leader', { ifAvailable: true }, async (lock) => {
if (!lock) {
console.log('Ein anderer Tab synchronisiert bereits.');
return;
}
console.log('Dieser Tab ist jetzt Leader.');
// Sperre halten, bis der Tab geschlossen wird
await new Promise(() => {});
});
startSyncIfLeader();
Wartende Anfrage mit Zeitlimit abbrechen
try {
await navigator.locks.request(
'db-migration',
{ signal: AbortSignal.timeout(3000) },
async () => {
await runMigration();
}
);
} catch (err) {
if (err.name === 'TimeoutError' || err.name === 'AbortError') {
console.warn('Sperre nicht innerhalb von 3 s erhalten.');
} else {
throw err;
}
}
async function runMigration() {
console.log('Migration läuft …');
}
Sperrzustand mit query() und geteilte Sperren
// Zwei Leser gleichzeitig mit geteilter Sperre
const read = (id) =>
navigator.locks.request('cache', { mode: 'shared' }, async () => {
await new Promise((r) => setTimeout(r, 500));
return `Leser ${id} fertig`;
});
const readers = [read(1), read(2)];
const { held, pending } = await navigator.locks.query();
console.log('Gehalten:', held.map((l) => `${l.name}/${l.mode}`));
console.log('Wartend:', pending.length);
console.log(await Promise.all(readers));