Hoppa till innehåll

Strömmar

Phasis implementerar hela WHATWG Streams Standard. Varje klass, varje läsarläge, varje hjälpfunktion. Samma kod som fungerar i Node och moderna webbläsare fungerar i Phasis.

Implementationen är den som fetch använder internt — Response.body är en riktig ReadableStream, Request tar emot strömmande kroppar och tee() som behövs för att klona en Response fungerar.

Det här ingår

Klass Status
ReadableStream Fullständig
ReadableStreamDefaultController Fullständig
ReadableByteStreamController Fullständig
ReadableStreamDefaultReader Fullständig
ReadableStreamBYOBReader Fullständig
ReadableStreamBYOBRequest Fullständig
WritableStream Fullständig
WritableStreamDefaultController Fullständig
WritableStreamDefaultWriter Fullständig
TransformStream Fullständig
TransformStreamDefaultController Fullständig
ByteLengthQueuingStrategy Fullständig
CountQueuingStrategy Fullständig

Dessutom stöds metoderna du oftast använder: pipeTo, pipeThrough, tee, getReader, asynkron iteration, Symbol.asyncIterator, BYOB-läsning med { min }.

Producera

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

Konsumera

Tre sätt att läsa:

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

Transformera

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

Bytströmmar (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 } stöds — read(view, { min: 2 }) slutförs först när minst 2 element är tillgängliga.

Bakåttryck

desiredSize och ready fungerar enligt specifikationen. Använd en köstrategi för att ge producenten en återkopplingskanal:

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

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

Dela strömmen för cachelagring

Vanligt mönster: läs kroppen två gånger.

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

Response.clone() gör det åt dig och returnerar en ny Response.

Förbehåll

  • Endast synkrona producenter. En pull() som utför blockerande I/O med PHP blockerar skriptet. Använd transportkroken för fetch för HTTP; för filer är det bättre att läsa in dem i minnet i förväg. setTimeout / setInterval finns tillgängliga (se Händelseloop), men motorn använder virtuell tid som standard — en setTimeout(cb, 1000) inuti en pull() väntar inte PHP, utan flyttar fram loopklockan. För pacing i realtid måste värden driva tickEventLoop() mot en väggklocka.

Se även