
# Strömmar

Phasis implementerar hela [WHATWG Streams Standard](https://streams.spec.whatwg.org/). 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

```js
// 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
```

```js
// 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:

```js
// 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

```js
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)

```js
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:

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

## Med fetch

```js
// 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.

```js
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](https://phasis.inline0.dev/sv/docs/api#event-loop)), 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

- [Web API:er (WPT)](https://phasis.inline0.dev/sv/docs/compatibility/web-apis) — tabell över WPT-godkännandefrekvens för Streams-deltester.
- [Fetch-transport](https://phasis.inline0.dev/sv/docs/interop/fetch-transport) — hur förfrågningars och svarens kroppar passerar gränsen mellan PHP/JS.
- [AbortController-mönster](https://phasis.inline0.dev/sv/docs/interop/abort-patterns) — avbryta läsningar som pågår.
