Preskoči na vsebino

Tokovi

Phasis implementira celoten standard WHATWG Streams Standard. Vsak razred, vsak način branja, vsaka pomožna funkcija. Ista koda, ki deluje v Node in sodobnih brskalnikih, deluje tudi v Phasis.

Implementacija je tista, ki jo fetch uporablja interno — Response.body je pravi ReadableStream, Request sprejema telesa v toku, deluje pa tudi tee(), ki ga potrebujete za kloniranje Response.

Kaj je vključeno

Razred Stanje
ReadableStream Polno
ReadableStreamDefaultController Polno
ReadableByteStreamController Polno
ReadableStreamDefaultReader Polno
ReadableStreamBYOBReader Polno
ReadableStreamBYOBRequest Polno
WritableStream Polno
WritableStreamDefaultController Polno
WritableStreamDefaultWriter Polno
TransformStream Polno
TransformStreamDefaultController Polno
ByteLengthQueuingStrategy Polno
CountQueuingStrategy Polno

Poleg tega so na voljo tudi metode, ki jih najpogosteje uporabljate: pipeTo, pipeThrough, tee, getReader, asinhrona iteracija, Symbol.asyncIterator, branje BYOB z { min }.

Ustvarjanje

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();
  },
});

Branje

Trije načini branja:

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)]);

Preoblikovanje

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

Tokovi bajtov (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 podprt — read(view, { min: 2 }) se razreši šele, ko je na voljo vsaj 2 elementa.

Povratni pritisk

desiredSize in ready delujeta, kot določa specifikacija. Strategijo čakanja v vrsti uporabite za povratni kanal do ustvarjalca:

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

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 });

Tee za predpomnjenje

Pogost vzorec: telo preberite dvakrat.

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

Response.clone() to opravi namesto vas in vrne svež Response.

Omejitve

  • Ustvarjalci delujejo samo sinhrono. pull(), ki izvaja blokirajoči V/I z PHP, blokira skript. Za HTTP uporabite kavelj prenosa fetch; pri datotekah jih raje vnaprej preberite v pomnilnik. Na voljo sta setTimeout / setInterval (glejte Zanka dogodkov), vendar mehanizem privzeto uporablja virtualni čas — setTimeout(cb, 1000) znotraj pull() ne povzroči dejanskega spanja, PHP premakne uro zanke naprej. Za ritem v realnem času mora gostitelj upravljati tickEventLoop() glede na stensko uro.

Glejte tudi