Start · Sprachen · JavaScript · Referenz · LockManager.request()

LockManager.request()

Methode

Fordert einen benannten Lock an, führt den Callback aus, sobald der Lock gewährt wurde, und gibt ihn automatisch frei, wenn der vom Callback zurückgegebene Promise erfüllt ist.

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

Signatur

LockManager.prototype.request(name, options?, callback)

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 mit null aufgerufen.
  • steal: Bestehende Locks mit diesem Namen werden gewaltsam freigegeben.
  • signal: Mit einem AbortSignal brichst 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

Typ
Promise<any>
Beschreibung
Ein Promise, das mit dem Rückgabewert des Callbacks erfüllt wird, nachdem der Lock freigegeben wurde. Er wird abgelehnt, wenn der Callback einen Fehler wirft, die Anfrage per 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);
Token: eyJhbGciOi...

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();
Dieser Tab ist jetzt Leader. (in weiteren Tabs:) Ein anderer Tab ist bereits Leader.

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');
Lese … Lese … Schreibe 42 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;
  }
}
Export läuft

// Wichtig · Fallstricke

  • Nur in sicheren Kontexten: navigator.locks gibt 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 navigator prü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 await oder return vergessen 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" und signal zusammen mit steal oder ifAvailable lösen einen NotSupportedError aus. Namen mit führendem - sind reserviert.
  • steal mit Vorsicht: Beim bisherigen Halter wird das Promise von request() mit AbortError abgelehnt. Sein Code läuft aber weiter, der gegenseitige Ausschluss ist also nicht mehr garantiert. Nutze steal nur 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.