Start · Sprachen · JavaScript · Referenz · ReadableStreamDefaultReader.releaseLock()

ReadableStreamDefaultReader.releaseLock()

Methode

Gibt die Sperre frei, die der Reader auf seinen <code>ReadableStream</code> hält. Danach kann der Stream von einem anderen Reader oder per <code>pipeTo()</code> gelesen werden.

seit JavaScript Baseline: alle modernen Browser (seit 2019/2020), Kategorie: webapi

Signatur

ReadableStreamDefaultReader.prototype.releaseLock()

Beschreibung

Sobald ein Stream mit stream.getReader() einen Reader bekommt, ist er gesperrt (stream.locked === true). Solange die Sperre besteht, kann niemand sonst aus dem Stream lesen: Weitere getReader()-Aufrufe, pipeTo(), pipeThrough() oder tee() lösen einen TypeError aus. Mit releaseLock() gibt der Reader den Stream wieder frei.

Das braucht man vor allem, wenn ein Stream teilweise gelesen und danach weitergereicht werden soll, z. B. um einen Dateikopf oder die ersten Bytes einer fetch()-Antwort zu prüfen und den Rest dann an eine andere Funktion oder eine Pipe-Kette zu übergeben. Daten, die noch nicht gelesen wurden, bleiben im Stream erhalten. Der nächste Reader macht genau dort weiter.

Nach dem Freigeben ist der Reader nicht mehr nutzbar: Weitere read()-Aufrufe werden mit einem TypeError abgelehnt, und das Promise reader.closed wird ebenfalls mit einem TypeError abgelehnt. Der Stream selbst wird durch releaseLock() nicht geschlossen oder abgebrochen. Wer den Stream beenden will, nutzt reader.cancel().

Laufen beim Aufruf noch offene read()-Anfragen, werden deren Promises nach aktueller Spezifikation mit einem TypeError abgelehnt. Ist der Reader schon freigegeben, macht ein erneuter Aufruf nichts.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Die Methode wirkt nur als Nebeneffekt: Danach gilt stream.locked === false.

Beispiele

Kopfzeile lesen und den Rest an einen neuen Reader übergeben

const stream = new ReadableStream({
  start(controller) {
    controller.enqueue('HEADER:v2');
    controller.enqueue('Datensatz 1');
    controller.enqueue('Datensatz 2');
    controller.close();
  }
});

const peekHeader = async (s) => {
  const reader = s.getReader();
  const { value } = await reader.read();
  reader.releaseLock(); // Stream für andere Konsumenten freigeben
  return value;
};

const header = await peekHeader(stream);
console.log(header);
console.log('gesperrt?', stream.locked);

// Neuer Reader liest ab der zweiten Stelle weiter
const reader2 = stream.getReader();
while (true) {
  const { done, value } = await reader2.read();
  if (done) break;
  console.log(value);
}
HEADER:v2 gesperrt? false Datensatz 1 Datensatz 2

Sperre bei fetch() zuverlässig mit try/finally freigeben

const readFirstBytes = async (url, limit = 1024) => {
  const response = await fetch(url);
  const reader = response.body.getReader();
  const chunks = [];
  let total = 0;
  try {
    while (total < limit) {
      const { done, value } = await reader.read();
      if (done) break;
      chunks.push(value);
      total += value.byteLength;
    }
  } finally {
    // Auch bei Fehlern freigeben, damit der Body weiterverwendbar bleibt
    reader.releaseLock();
  }
  return { response, total };
};

const { response, total } = await readFirstBytes('https://example.com/');
console.log('gelesen:', total, 'Bytes; Body gesperrt?', response.body.locked);
gelesen: 1256 Bytes; Body gesperrt? false

Offene read()-Anfrage wird beim Freigeben abgelehnt

// Stream, der nie Daten liefert
const stream = new ReadableStream();
const reader = stream.getReader();

const pending = reader.read(); // wartet für immer
reader.releaseLock();

try {
  await pending;
} catch (err) {
  console.log(err.name);
}

await reader.closed.catch((err) => console.log('closed:', err.name));
console.log('locked:', stream.locked);
TypeError closed: TypeError locked: false

// Wichtig · Fallstricke

  • Geändertes Verhalten: Ältere Browser (etwa Chrome vor 106, Firefox vor 102, Safari vor 16.4) werfen beim Aufruf einen TypeError, wenn noch read()-Anfragen offen sind. Neuere Browser lehnen stattdessen die offenen Promises ab. Wenn der Code in beiden Fällen funktionieren soll, erst alle read()-Promises abwarten und danach freigeben.
  • releaseLock() schließt den Stream nicht. Wer nicht mehr lesen will, ruft reader.cancel() auf. Sonst bleibt z. B. eine Netzwerkverbindung unnötig offen.
  • Nach dem Freigeben den alten Reader nicht weiterverwenden: read() und cancel() werden dann mit einem TypeError abgelehnt.
  • for await (const chunk of stream) gibt die Sperre am Ende oder bei break automatisch frei. Dabei wird der Stream aber standardmäßig abgebrochen, außer man nutzt stream.values({ preventCancel: true }).
  • Die Freigabe gehört in einen finally-Block. Sonst bleibt ein Stream nach einem Fehler dauerhaft gesperrt, und z. B. response.text() scheitert mit „body stream is locked“.