ReadableStream
ReadableStream represents a stream of data that can be read incrementally rather than all at once.
In Scripting, ReadableStream<Data> is used in various scenarios, including:
- Handling streaming HTTP responses (e.g.,
Response.body)
- Chunked file downloads or large data transfers
- Real-time data streams such as logs, AI model output, or event streams
It follows the same behavior as the standard Web Streams API, allowing asynchronous iteration (for await...of) and manual reading via a ReadableStreamDefaultReader.
Definition
1class ReadableStream<T = any> {
2 constructor(underlyingSource?: UnderlyingSource<T>)
3
4 get locked(): boolean
5 cancel(reason?: any): Promise<void>
6 getReader(): ReadableStreamDefaultReader<T>
7 tee(): [ReadableStream<T>, ReadableStream<T>]
8}
Overview
- A
ReadableStream represents a data source that can be consumed asynchronously.
- Instead of loading the entire payload into memory, data is read in chunks as it becomes available.
- Only one active reader can consume a stream at a time. When locked (
locked = true), no other consumer can access it until released or canceled.
Properties
locked: boolean
Indicates whether the stream is currently locked to a reader.
If true, you must release the lock or cancel the stream before another reader can be created.
Example
1const reader = response.body.getReader()
2console.log(response.body.locked) // true
Methods
getReader(): ReadableStreamDefaultReader<T>
Returns a ReadableStreamDefaultReader that allows manual, incremental reading of stream data.
Each call to reader.read() returns a Promise resolving to an object { value, done }.
Example
1const reader = response.body.getReader()
2
3while (true) {
4 const { done, value } = await reader.read()
5 if (done) break
6 console.log("Received chunk:", value)
7}
cancel(reason?: any): Promise<void>
Cancels reading from the stream.
Optionally provide a reason describing why the operation was aborted.
Example
1const reader = response.body.getReader()
2await response.body.cancel("User canceled reading")
tee(): [ReadableStream<T>, ReadableStream<T>]
Splits a stream into two identical branches that can be consumed independently.
Example
1const [stream1, stream2] = response.body.tee()
2
3const reader1 = stream1.getReader()
4const reader2 = stream2.getReader()
ReadableStreamDefaultReader
When you obtain a reader using getReader(), you can manually control the reading process.
Definition
1interface ReadableStreamDefaultReader<T> {
2 read(): Promise<{ value: T; done: boolean }>
3 releaseLock(): void
4 cancel(reason?: any): Promise<void>
5}
Method Descriptions
| Method |
Description |
| read() |
Reads the next data chunk. Resolves with { value, done }. When done = true, the stream has ended. |
| releaseLock() |
Releases the lock so the stream can be read again by another consumer. |
| cancel(reason?) |
Cancels reading from the stream. |
Example — Reading Stream Data
1const reader = response.body.getReader()
2
3while (true) {
4 const { done, value } = await reader.read()
5 if (done) break
6
7 // Each chunk is a Data instance
8 const text = value.toRawString()
9 console.log("Chunk:", text)
10}
11
12reader.releaseLock()
Integration with Response
The Response.body property is a ReadableStream<Data> that allows streaming response content.
Example — Handling Streaming Network Response
1const response = await fetch("https://example.com/stream")
2
3const reader = response.body.getReader()
4while (true) {
5 const { done, value } = await reader.read()
6 if (done) break
7 console.log("Received:", value.toRawString())
8}
This method enables real-time data processing before the full response is received, ideal for:
- Real-time log streaming
- Progressive file downloads
- AI or LLM text generation (token streaming)
Integration with Data
In Scripting, each stream chunk (value) is a Data object.
You can use the Data APIs such as .toRawString() or .toUint8Array() to inspect or transform the content.
Example — Save Stream Data to a File
1const reader = response.body.getReader()
2const chunks: Data[] = []
3
4while (true) {
5 const { done, value } = await reader.read()
6 if (done) break
7 chunks.push(value)
8}
9
10const fileData = Data.combine(chunks)
11FileManager.write(fileData, "/local/download.bin")
Using Async Iteration
ReadableStream supports async iteration (for await...of), simplifying data consumption syntax:
1for await (const chunk of response.body) {
2 console.log("Chunk size:", chunk.size)
3}
This approach automatically handles the done condition for you, resulting in cleaner code.
Common Use Cases
| Use Case |
Example |
| Large File Download |
Stream file chunks from the network and save locally without high memory usage. |
| AI Output Streaming |
Display real-time model output as it’s generated. |
| Incremental Local Processing |
Process local files or streams incrementally. |
Notes
- Single-reader rule: Only one reader can consume a
ReadableStream at a time.
- Memory efficiency: Streaming avoids loading large payloads entirely into memory.
- Error handling: Reading may reject on errors (e.g., network failure). Use
try...catch to handle exceptions safely.
- Chunk type: For
Response.body, each chunk is a Data object — not plain text or byte arrays.
Summary
ReadableStream is a core component of Scripting’s streaming data architecture, providing efficient and flexible data handling capabilities.
Key Features
- Asynchronous, incremental data reading
- Seamless integration with
fetch(), Response, and Data
- Supports real-time processing and large data transfers
- Fully compatible with the standard Web Streams API