Back to Wasm Bindgen

WebAssembly JSPI + Fetch Streaming example

examples/jspi-fetch-streams/index.html

0.2.1282.5 KB
Original Source

WebAssembly JSPI + Fetch Streaming example

JSPI lets plain (non-async) Rust suspend a WASM fiber while awaiting a JS Promise — without blocking the event loop. This example applies that to the Streams API: each reader.read() Promise suspends the fiber until the next chunk arrives.

WebAssembly.SuspendingResponse streamingRequest body streamingWASM module

How it works

drain_stream is a plain Rust helper (no async, no .await) that loops calling block_on_promise(&reader.read()), suspending the JSPI fiber on each chunk:

fndrain\_stream(stream: ReadableStream) -> Result<(u32, u32), JsValue> {letreader: ReadableStreamDefaultReader = stream.get\_reader().unchecked\_into();let(muttotal,mutchunks) = (0u32, 0u32);loop{// Suspends the JSPI fiber until the next chunk (or done) resolves.letresult: ReadableStreamReadResult =block\_on\_promise(&reader.read())?.unchecked\_into();ifresult.get\_done().unwrap\_or(true) {break; }letchunk: Uint8Array = result.get\_value().unchecked\_into();
        total += chunk.length();
        chunks += 1;
    }Ok((total, chunks))
}

This helper powers two exports:

exportdescriptionbrowser support
read_stream(stream)reads any ReadableStream from Rust — request body, synthetic stream, etc.Chrome, Firefox, Safari
fetch_stream(url)fetches url and streams the response bodyChrome, Firefox, Safari

Request body streaming Chrome only

Sending a ReadableStream as a fetch request body requires duplex: "half" on the RequestInit. Firefox and Safari do not support this; they throw a TypeError when a ReadableStream is used as the request body.

Detection pattern (works in all browsers without throwing):

letsupported =false;try{newRequest('', {
        body:newReadableStream(),
        method:'POST',getduplex() { supported =true;return'half'; },
    });
}catch{}// supported === true ⟹ Chrome/Chromium// supported === false ⟹ Firefox / Safari

On the Service Worker / server side (Rust receiving the request), the fallback is to buffer the body via arrayBuffer() when request.body is null (Firefox), and to use ReadableStream chunk-by-chunk via block_on_promise when it is present (Chrome).

Live demo

① Fetch + stream response② Read a ReadableStream③ Request streaming support?④ Concurrent fibers▶ Run all

clear