Signatur
Beschreibung
navigator.locks.request() ist das Herzstück der Web Locks API. Damit koordinierst du den Zugriff auf gemeinsam genutzte Ressourcen (z. B. IndexedDB, Tokens, Synchronisations-Vorgänge) zwischen mehreren Tabs, Fenstern, iframes und Workern derselben Herkunft (Origin). Ein Lock wird über einen frei wählbaren Namen (String) identifiziert. Der Browser verwaltet eine Warteschlange pro Name.
Sobald der Lock gewährt wurde, wird der callback mit einem Lock-Objekt aufgerufen. Der Lock bleibt so lange gehalten, bis der Callback fertig ist. Bei einer async-Funktion ist das der Fall, wenn ihr Promise erfüllt oder abgelehnt wurde. Danach wird der Lock automatisch freigegeben, und der nächste Anfragende in der Warteschlange ist an der Reihe. Eine manuelle „release“-Methode gibt es nicht.
Über options steuerst du das Verhalten:
mode: "exclusive"(Standard): Es kann immer nur einer den Lock halten.mode: "shared": Mehrere Halter gleichzeitig sind möglich, etwa für Lesezugriffe (Reader-Writer-Muster).ifAvailable: Der Lock wird nur gewährt, wenn er sofort frei ist. Andernfalls wird der Callback mitnullaufgerufen.steal: Bestehende Locks mit diesem Namen werden gewaltsam freigegeben.signal: Mit einemAbortSignalbrichst du das Warten ab.
Typische Einsatzfälle: Nur ein Tab soll im Hintergrund synchronisieren („Leader Election“), ein Access-Token soll nicht parallel erneuert werden, oder Schreibzugriffe auf lokale Speicher sollen nacheinander ablaufen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $name Pflicht | string | Name des Locks. Er gilt für die gesamte Herkunft (Origin). Namen, die mit - beginnen, sind reserviert und lösen einen NotSupportedError aus. |
|
| $options | object | {} | Optionales Konfigurationsobjekt mit folgenden Eigenschaften: mode ("exclusive" | "shared", Standard "exclusive"), ifAvailable (boolean, Standard false), steal (boolean, Standard false, nur mit "exclusive") und signal (AbortSignal). steal und ifAvailable dürfen nicht zusammen verwendet werden, signal nicht zusammen mit steal oder ifAvailable. |
| $callback Pflicht | (lock: Lock | null) => any | Promise<any> | Wird aufgerufen, sobald der Lock gewährt wurde. Mit ifAvailable: true wird sie mit null aufgerufen, falls der Lock nicht sofort verfügbar ist. Der Lock wird gehalten, bis der zurückgegebene Wert bzw. Promise abgeschlossen ist. |
Rückgabewert
signal abgebrochen wird (AbortError) oder der Lock per steal entzogen wird.Beispiele
Exklusiver Zugriff: Token-Refresh nur einmal gleichzeitig
const refreshToken = async () => {
return navigator.locks.request('auth-token-refresh', async (lock) => {
// Nur ein Tab/Worker führt diesen Block gleichzeitig aus
const cached = localStorage.getItem('token');
if (cached && !isExpired(cached)) return cached;
const res = await fetch('/api/refresh', { method: 'POST' });
const { token } = await res.json();
localStorage.setItem('token', token);
return token;
});
};
const isExpired = (token) => false; // vereinfachte Prüfung
const token = await refreshToken();
console.log('Token:', token);
Nicht blockierend mit ifAvailable: Leader-Tab bestimmen
const tryBecomeLeader = () =>
navigator.locks.request('sync-leader', { ifAvailable: true }, async (lock) => {
if (!lock) {
console.log('Ein anderer Tab ist bereits Leader.');
return false;
}
console.log('Dieser Tab ist jetzt Leader.');
// Lock bleibt gehalten, solange dieser Promise offen ist
await new Promise(() => {}); // läuft, bis der Tab geschlossen wird
});
tryBecomeLeader();
Shared- und Exclusive-Modus (Reader/Writer)
const readData = () =>
navigator.locks.request('db', { mode: 'shared' }, async () => {
console.log('Lese …');
await new Promise((r) => setTimeout(r, 500));
return 'Daten';
});
const writeData = (value) =>
navigator.locks.request('db', { mode: 'exclusive' }, async () => {
console.log('Schreibe', value);
await new Promise((r) => setTimeout(r, 200));
});
// Zwei Leser laufen parallel, der Schreiber wartet, bis beide fertig sind
await Promise.all([readData(), readData(), writeData(42)]);
console.log('Fertig');
Warten mit Zeitlimit per AbortSignal
try {
await navigator.locks.request(
'export-job',
{ signal: AbortSignal.timeout(3000) },
async () => {
console.log('Export läuft');
await new Promise((r) => setTimeout(r, 1000));
}
);
} catch (err) {
if (err.name === 'AbortError' || err.name === 'TimeoutError') {
console.log('Lock nicht innerhalb von 3 s erhalten.');
} else {
throw err;
}
}
// Wichtig · Fallstricke
- Nur in sicheren Kontexten:
navigator.locksgibt es nur unter HTTPS bzw. localhost. Die Methode ist im Fenster und in Web-Workern verfügbar. Browser-Unterstützung: Chrome/Edge 69+/79+, Firefox 96+, Safari 15.4+. Vorher mit'locks' in navigatorprüfen. - Kein Freigeben von Hand: Der Lock hängt an der Lebensdauer des Callbacks. Gibt der Callback kein Promise zurück (z. B. weil du
awaitoderreturnvergessen hast), wird der Lock sofort wieder freigegeben. Das führt oft zu schwer auffindbaren Race Conditions. - Deadlocks: Forderst du innerhalb eines gehaltenen Locks denselben Namen erneut exklusiv an, wartet die Anfrage für immer. Die Locks sind nicht reentrant.
- Ungültige Kombinationen:
steal+ifAvailable,steal+mode: "shared"undsignalzusammen mitstealoderifAvailablelösen einenNotSupportedErroraus. Namen mit führendem-sind reserviert. - steal mit Vorsicht: Beim bisherigen Halter wird das Promise von
request()mitAbortErrorabgelehnt. Sein Code läuft aber weiter, der gegenseitige Ausschluss ist also nicht mehr garantiert. Nutzestealnur zur Wiederherstellung nach Fehlern. - Locks gelten nur innerhalb derselben Herkunft (Origin) und desselben Speicher-Bereichs (Storage Partition). Sie werden freigegeben, wenn der Tab oder Worker beendet wird. Über Browser oder Geräte hinweg funktionieren sie nicht.
- Zur Fehlersuche liefert
navigator.locks.query()eine Momentaufnahme gehaltener und wartender Locks.