Start · Sprachen · JavaScript · Referenz · WebTransport.incomingUnidirectionalStreams

WebTransport.incomingUnidirectionalStreams

Eigenschaft

Schreibgeschützter <code>ReadableStream</code>, der für jeden vom Server geöffneten unidirektionalen Stream einen <code>WebTransportReceiveStream</code> liefert.

seit JavaScript Chrome/Edge 97+, Firefox 114+, Safari 26.4+; nur i Kategorie: webapi

Signatur

WebTransport.incomingUnidirectionalStreams

Beschreibung

Die Eigenschaft incomingUnidirectionalStreams einer WebTransport-Verbindung ist ein ReadableStream, dessen einzelne Einträge selbst wieder Streams sind: Jedes Mal, wenn der Server einen neuen unidirektionalen Stream öffnet (Server → Client), taucht hier ein WebTransportReceiveStream auf – ein ReadableStream mit Bytes (Uint8Array-Blöcken).

Unidirektionale Streams eignen sich für Daten, die nur in eine Richtung fließen, z. B. Push-Nachrichten, Spielzustände, Log-Ströme oder Mediensegmente. Anders als Datagramme sind sie zuverlässig und geordnet – innerhalb eines Streams. Mehrere Streams laufen dank QUIC unabhängig voneinander, sodass ein verlorenes Paket in einem Stream die anderen nicht blockiert (kein Head-of-Line-Blocking).

Typischerweise liest man die Eigenschaft in einer Schleife mit getReader() oder for await...of aus und verarbeitet jeden eingehenden Stream in einer eigenen, nicht abgewarteten asynchronen Funktion, damit ein langsamer Stream die Annahme weiterer Streams nicht aufhält. Für die Gegenrichtung (Client → Server) nutzt man WebTransport.createUnidirectionalStream().

Rückgabewert

Typ
ReadableStream<WebTransportReceiveStream>
Beschreibung
Ein ReadableStream, dessen Einträge WebTransportReceiveStream-Objekte sind (je ein vom Server geöffneter unidirektionaler Stream). Er wird geschlossen, wenn die Verbindung endet, bzw. mit Fehler abgebrochen, wenn sie fehlschlägt.

Beispiele

Eingehende Streams mit getReader() annehmen

const transport = new WebTransport('https://example.com:4433/push');
await transport.ready;

const readStream = async (stream) => {
  const reader = stream.getReader();
  const decoder = new TextDecoder();
  let text = '';
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    text += decoder.decode(value, { stream: true });
  }
  text += decoder.decode();
  console.log('Stream empfangen:', text);
};

const streams = transport.incomingUnidirectionalStreams.getReader();
while (true) {
  const { value: stream, done } = await streams.read();
  if (done) {
    console.log('Keine weiteren Streams.');
    break;
  }
  // Nicht abwarten: parallel weitere Streams annehmen
  readStream(stream).catch((err) => console.error('Stream-Fehler:', err));
}
Stream empfangen: {"type":"news","id":1} Stream empfangen: {"type":"news","id":2} Keine weiteren Streams.

Kompakt mit for await...of und JSON-Nachrichten

const transport = new WebTransport('https://game.example.com:4433/state');
await transport.ready;

const handleStream = async (stream) => {
  const text = await new Response(stream).text();
  const update = JSON.parse(text);
  console.log('Spielzustand:', update.tick, update.players.length);
};

// ReadableStream ist in modernen Browsern async-iterierbar
for await (const stream of transport.incomingUnidirectionalStreams) {
  handleStream(stream).catch(console.error);
}
Spielzustand: 101 4 Spielzustand: 102 4

Verbindungsende und Abbruch sauber behandeln

const transport = new WebTransport('https://example.com:4433/logs');

transport.closed
  .then(({ closeCode, reason }) => console.log('Geschlossen:', closeCode, reason))
  .catch((err) => console.warn('Verbindung abgebrochen:', err.message));

try {
  await transport.ready;
  const reader = transport.incomingUnidirectionalStreams.getReader();
  const { value: stream } = await reader.read();
  // Nur den ersten Stream lesen, danach nicht mehr interessiert
  await stream.cancel('Nicht mehr benötigt');
  reader.releaseLock();
  transport.close({ closeCode: 0, reason: 'Fertig' });
} catch (err) {
  console.error('Fehler:', err);
}
Geschlossen: 0 Fertig

// Wichtig · Fallstricke

  • Parallel verarbeiten: Wird jeder eingehende Stream mit await vollständig gelesen, bevor der nächste angenommen wird, stauen sich Streams an – und der Server kann durch Flusskontrolle blockiert werden. Einzelne Streams daher in eigenen Promises verarbeiten.
  • Nur ein Reader: Der Stream kann nur von einem Reader gleichzeitig gesperrt werden; ein zweites getReader() wirft einen TypeError, solange der erste nicht per releaseLock() freigegeben ist.
  • Fehler: Bricht die Verbindung ab, wird der Stream mit einem Fehler (meist WebTransportError) beendet – read() bzw. for await werfen dann. Immer try/catch bzw. transport.closed behandeln.
  • Kompatibilität: Nur in sicheren Kontexten (HTTPS) verfügbar, erfordert einen HTTP/3-Server mit WebTransport-Unterstützung. Chromium seit Version 97, Firefox seit 114; Safari erst in neueren Versionen – vorher per 'WebTransport' in globalThis prüfen. Asynchrone Iteration über ReadableStream (for await) ist in Safari erst spät verfügbar; im Zweifel getReader() nutzen.
  • Streams, die man nicht braucht, mit stream.cancel() beenden, damit der Server keine Ressourcen verschwendet.