Preskočiť na obsah

Prúdy

Phasis implementuje celý štandard WHATWG Streams Standard. Každá trieda, každý režim čítačky a každá pomocná funkcia. Rovnaký kód, ktorý funguje v Node a moderných prehliadačoch, funguje aj v Phasis.

Implementácia je tá, ktorú fetch používa interne — Response.body je skutočný ReadableStream, Request prijíma streamované telá a funguje aj tee(), ktorý potrebujete na klonovanie Response.

Čo je súčasťou

Trieda Stav
ReadableStream Úplné
ReadableStreamDefaultController Úplné
ReadableByteStreamController Úplné
ReadableStreamDefaultReader Úplné
ReadableStreamBYOBReader Úplné
ReadableStreamBYOBRequest Úplné
WritableStream Úplné
WritableStreamDefaultController Úplné
WritableStreamDefaultWriter Úplné
TransformStream Úplné
TransformStreamDefaultController Úplné
ByteLengthQueuingStrategy Úplné
CountQueuingStrategy Úplné

A tiež metódy, ktoré sa najčastejšie používajú: pipeTo, pipeThrough, tee, getReader, asynchrónna iterácia, Symbol.asyncIterator, čítanie BYOB pomocou { min }.

Tvorba

JavaScript
// Pull-based — only generates values when read
const counter = new ReadableStream({
  start(controller) { this.n = 0; },
  pull(controller) {
    if (this.n >= 5) { controller.close(); return; }
    controller.enqueue(this.n++);
  },
});

for await (const v of counter) console.log(v);  // 0,1,2,3,4
JavaScript
// Push-based — start enqueues everything eagerly
const small = new ReadableStream({
  start(c) {
    [10, 20, 30].forEach(n => c.enqueue(n));
    c.close();
  },
});

Čítanie

Tri spôsoby čítania:

JavaScript
// 1. Reader
const reader = stream.getReader();
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  console.log(value);
}
reader.releaseLock();

// 2. Async iteration (preferred)
for await (const chunk of stream) {
  console.log(chunk);
}

// 3. tee + parallel pipes
const [a, b] = stream.tee();
await Promise.all([a.pipeTo(sinkA), b.pipeTo(sinkB)]);

Transformácia

JavaScript
const upper = new TransformStream({
  transform(chunk, controller) {
    controller.enqueue(chunk.toUpperCase());
  },
});

const result = new ReadableStream({
  start(c) { ['hi', 'there'].forEach(s => c.enqueue(s)); c.close(); }
}).pipeThrough(upper);

for await (const v of result) console.log(v);  // HI, THERE

Bajtové prúdy (BYOB)

JavaScript
const bytes = new ReadableStream({
  type: 'bytes',
  start(controller) {
    controller.enqueue(new Uint8Array([1, 2, 3, 4, 5, 6]));
    controller.close();
  },
});

const reader = bytes.getReader({ mode: 'byob' });
const buf = new Uint8Array(4);
const { value, done } = await reader.read(buf);
// value is a Uint8Array of length 4, sharing buffer with `buf`

{ min: N } je podporované — read(view, { min: 2 }) sa vyrieši až vtedy, keď sú k dispozícii aspoň 2 prvky.

Spätný tlak

desiredSize aj ready fungujú podľa špecifikácie. Použite stratégiu zaraďovania do frontu, ktorá poskytne producentovi spätnú väzbu:

JavaScript
const stream = new ReadableStream({
  pull(controller) {
    if (controller.desiredSize <= 0) return;  // back off
    controller.enqueue(nextChunk());
  },
}, new CountQueuingStrategy({ highWaterMark: 4 }));

Použitie s fetch

JavaScript
// Response body is a ReadableStream
const res = await fetch('https://example.com/big.txt');
let total = 0;
for await (const chunk of res.body) {
  total += chunk.byteLength;
}

// Request bodies can stream too
const upload = new ReadableStream({
  start(c) { c.enqueue(new TextEncoder().encode('hello')); c.close(); }
});
await fetch('https://example.com/upload', { method: 'POST', body: upload });

Rozdvojenie na účely ukladania do vyrovnávacej pamäte

Bežný vzor: prečítať telo dvakrát.

JavaScript
const res = await fetch(url);
const [forCache, forUse] = res.body.tee();
await Promise.all([
  forCache.pipeTo(cacheWritable),
  parseJson(forUse),
]);

Response.clone() to urobí za vás a vráti nový Response.

Upozornenia

  • Len synchronickí producenti. Objekt pull(), ktorý vykonáva blokujúce V/V operácie PHP, zablokuje skript. Pre HTTP použite hák prenosu fetch; pri súboroch ich radšej vopred načítajte do pamäte. K dispozícii sú setTimeout / setInterval (pozri Slučka udalostí), no predvolene používa engine virtuálny čas — setTimeout(cb, 1000) vnútri pull() nespôsobí skutočný spánok PHP, ale posunie hodiny slučky. Načasovanie v reálnom čase vyžaduje, aby hostiteľ riadil tickEventLoop() podľa hodín reálneho času.

Pozrite si aj

  • Web API (WPT) — tabuľka úspešnosti testov WPT pokrývajúca podtesty Streams.
  • Prenos Fetch — ako telá požiadaviek a odpovedí prechádzajú cez hranicu medzi PHP a JS.
  • Vzory AbortController — zrušenie prebiehajúceho čítania.