Start · Sprachen · JavaScript · Referenz · Navigator.locks

Navigator.locks

Eigenschaft

Die schreibgeschützte Eigenschaft <code>navigator.locks</code> liefert den <code>LockManager</code>, mit dem man Web Locks anfordert und den Sperrzustand abfragt.

seit JavaScript Baseline 2022: Chrome 69, Edge 79, Firefox 96, Saf Kategorie: webapi

Signatur

get Navigator.locks

Beschreibung

navigator.locks ist der Einstiegspunkt der Web Locks API. Die Eigenschaft ist schreibgeschützt und gibt ein LockManager-Objekt zurück. Es hat zwei Methoden: request() fordert eine benannte Sperre an, query() liefert eine Momentaufnahme der gehaltenen und wartenden Sperren.

Web Locks koordinieren den Zugriff auf gemeinsam genutzte Ressourcen wie IndexedDB, localStorage oder einen Netzwerk-Sync über alle Tabs, Fenster und Worker desselben Ursprungs hinweg. Typische Fälle: Nur ein Tab soll eine WebSocket-Verbindung halten („Leader Election“), eine Datenbankmigration darf nur einmal laufen, oder Schreibzugriffe sollen nacheinander statt gleichzeitig passieren.

Eine Sperre gilt so lange, bis das Promise, das der Callback von request() zurückgibt, erfüllt oder abgelehnt ist. Danach wird sie automatisch freigegeben. Schließt man den Tab oder beendet den Worker, gibt der Browser die Sperre ebenfalls frei. Mit dem Modus "shared" können mehrere Leser dieselbe Sperre gleichzeitig halten, "exclusive" (Standard) lässt nur einen Halter zu.

In Workern steht dieselbe Schnittstelle als WorkerNavigator.locks (self.navigator.locks) zur Verfügung.

Rückgabewert

Typ
LockManager
Beschreibung
Das LockManager-Objekt des aktuellen Ursprungs mit den Methoden request(name, [options], callback) und query().

Beispiele

Exklusive Sperre für einen kritischen Abschnitt

const saveDraft = async (draft) => {
  // Nur ein Tab gleichzeitig darf den Entwurf schreiben
  return navigator.locks.request('draft-storage', async (lock) => {
    console.log(`Sperre erhalten: ${lock.name} (${lock.mode})`);
    const current = JSON.parse(localStorage.getItem('drafts') ?? '[]');
    current.push(draft);
    localStorage.setItem('drafts', JSON.stringify(current));
    return current.length;
  });
};

const count = await saveDraft({ text: 'Hallo Welt' });
console.log('Anzahl Entwürfe:', count);
Sperre erhalten: draft-storage (exclusive) Anzahl Entwürfe: 1

Leader Election: nur ein Tab hält die WebSocket-Verbindung

const becomeLeader = () =>
  navigator.locks.request('ws-leader', async () => {
    console.log('Dieser Tab ist jetzt Leader');
    const ws = new WebSocket('wss://example.com/live');
    // Sperre halten, bis die Verbindung geschlossen wird
    await new Promise((resolve) => ws.addEventListener('close', resolve));
    console.log('Verbindung beendet, Sperre wird freigegeben');
  });

becomeLeader(); // weitere Tabs warten automatisch, bis der Leader wegfällt
Dieser Tab ist jetzt Leader

Nicht warten: ifAvailable und Abbruch per AbortSignal

// Sofort aufgeben, wenn die Sperre belegt ist
await navigator.locks.request('sync', { ifAvailable: true }, async (lock) => {
  if (!lock) {
    console.log('Sync läuft bereits in einem anderen Tab');
    return;
  }
  console.log('Starte Sync …');
});

// Höchstens 2 Sekunden auf die Sperre warten
try {
  await navigator.locks.request(
    'sync',
    { signal: AbortSignal.timeout(2000) },
    async () => console.log('Sync nach Wartezeit gestartet')
  );
} catch (err) {
  if (err.name === 'TimeoutError') console.log('Zeitüberschreitung beim Warten');
}
Starte Sync … Sync nach Wartezeit gestartet

Sperrzustand mit query() untersuchen

if ('locks' in navigator) {
  const { held, pending } = await navigator.locks.query();
  held.forEach((l) => console.log('Gehalten:', l.name, l.mode));
  pending.forEach((l) => console.log('Wartend:', l.name, l.mode));
} else {
  console.log('Web Locks API nicht verfügbar');
}
Gehalten: ws-leader exclusive

// Wichtig · Fallstricke

  • Nur in sicheren Kontexten (HTTPS oder localhost): Auf unsicheren Seiten ist navigator.locks undefined. Vorher mit 'locks' in navigator prüfen.
  • Die Sperre wird erst freigegeben, wenn das vom Callback zurückgegebene Promise abgeschlossen ist. Gibt der Callback ein nie endendes Promise zurück, bleibt die Sperre dauerhaft belegt. Ein vergessenes await im Callback führt dagegen dazu, dass die Sperre zu früh freigegeben wird.
  • Deadlocks sind möglich, wenn man Sperren ineinander verschachtelt und dabei in unterschiedlicher Reihenfolge anfordert. Der Browser erkennt sie nicht.
  • Sperrnamen, die mit - beginnen, sind reserviert und lösen einen NotSupportedError aus. ifAvailable und steal lassen sich nicht zusammen verwenden. Beide sind auch nicht mit signal kombinierbar.
  • steal: true gibt bestehende Sperren gewaltsam frei. Deren request()-Promise wird dann mit einem AbortError abgelehnt. Das ist nur für Notfall-Wiederherstellung gedacht.
  • Sperren gelten pro Ursprung und Speicherpartition. Zwischen verschiedenen Ursprüngen oder einem normalen und einem privaten Fenster gibt es keine Koordination.
  • Ältere Browser (Safari vor 15.4, Firefox vor 96) unterstützen die API nicht. Dort braucht man einen Fallback, z. B. über BroadcastChannel oder storage-Events.