Documentation Index
Fetch the complete documentation index at: https://bun.zhcndoc.com/llms.txt
Use this file to discover all available pages before exploring further.
Streams 是处理二进制数据而无需一次性加载全部到内存中的重要抽象。它们通常用于读写文件、发送和接收网络请求以及处理大量数据。
Bun 实现了 Web API 中的 ReadableStream 和 WritableStream。
To create a ReadableStream:
const stream = new ReadableStream({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});
可以使用 for await 语法逐块读取 ReadableStream 的内容。
for await (const chunk of stream) {
console.log(chunk);
}
// hello
// world
直接 ReadableStream
Bun 实现了一个优化版本的 ReadableStream,避免了不必要的数据复制和队列管理逻辑。
在传统的 ReadableStream 中,数据块是被 入队 的。每个数据块都会被复制到一个队列中,直到流准备好发送更多数据时才会被读取。
const stream = new ReadableStream({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});
而直接 ReadableStream 中,数据块会直接写入流中。不会有排队,也无需将数据块复制到内存中。controller 的 API 也做了相应更新,不使用 .enqueue(),而是调用 .write。
const stream = new ReadableStream({
type: "direct",
pull(controller) {
controller.write("hello");
controller.write("world");
},
});
当使用直接 ReadableStream 时,目标端会处理所有的数据块队列。流的消费者将收到与传给 controller.write() 完全一致的内容,不进行任何编码或修改。
异步生成器流
Bun 也支持将异步生成器函数作为 Response 和 Request 的数据源。使用异步生成器创建一个从异步源获取数据的 ReadableStream。
const response = new Response(
(async function* () {
yield "hello";
yield "world";
})(),
);
await response.text(); // "helloworld"
你也可以直接使用 [Symbol.asyncIterator]。
const response = new Response({
[Symbol.asyncIterator]: async function* () {
yield "hello";
yield "world";
},
});
await response.text(); // "helloworld"
如果你需要对流进行更细粒度的控制,yield 将返回直接的 ReadableStream 控制器。
const response = new Response({
[Symbol.asyncIterator]: async function* () {
const controller = yield "hello";
await controller.end();
},
});
await response.text(); // "hello"
Bun.ArrayBufferSink
Bun.ArrayBufferSink 类是一个用于构造未知大小 ArrayBuffer 的高速增量写入器。
const sink = new Bun.ArrayBufferSink();
sink.write("h");
sink.write("e");
sink.write("l");
sink.write("l");
sink.write("o");
sink.end();
// ArrayBuffer(5) [ 104, 101, 108, 108, 111 ]
如果需要以 Uint8Array 格式获取数据,可以向 start 方法传入 asUint8Array 选项。
const sink = new Bun.ArrayBufferSink();
sink.start({
asUint8Array: true,
});
sink.write("h");
sink.write("e");
sink.write("l");
sink.write("l");
sink.write("o");
sink.end();
// Uint8Array(5) [ 104, 101, 108, 108, 111 ]
.write() 方法支持字符串、类型化数组、ArrayBuffer 和 SharedArrayBuffer。
sink.write("h");
sink.write(new Uint8Array([101, 108]));
sink.write(Buffer.from("lo").buffer);
sink.end();
一旦调用 .end(),就无法再向 ArrayBufferSink 写入数据。然而,在流缓冲的场景下,通常需要持续写入数据并定期调用 .flush() 将内容(例如写入到一个 WritableStream)。为支持此需求,可在构造时传入 stream: true。
const sink = new Bun.ArrayBufferSink();
sink.start({
stream: true,
});
sink.write("h");
sink.write("e");
sink.write("l");
sink.flush();
// ArrayBuffer(5) [ 108, 101, 108 ]
sink.write("l");
sink.write("o");
sink.flush();
// ArrayBuffer(5) [ 108, 111 ]
.flush() 方法返回缓冲数据(作为 ArrayBuffer,如果 asUint8Array: true 则返回 Uint8Array)并清空内部缓冲区。
如果需要手动设置内部缓冲区的大小(以字节为单位),请传入 highWaterMark:
const sink = new Bun.ArrayBufferSink();
sink.start({
highWaterMark: 1024 * 1024, // 1 MB
});
See Typescript Definitions
/**
* 快速增量写入器,调用 end() 后生成 `ArrayBuffer`。
*/
export class ArrayBufferSink {
constructor();
start(options?: {
asUint8Array?: boolean;
/**
* 预分配此大小的内部缓冲区
* 当数据块较小时,能显著提升性能
*/
highWaterMark?: number;
/**
* 在 {@link ArrayBufferSink.flush} 时返回写入数据为 `Uint8Array`。
* 写入将从缓冲区开头重新开始。
*/
stream?: boolean;
}): void;
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
/**
* 刷新内部缓冲区
*
* 如果 {@link ArrayBufferSink.start} 传入了 `stream` 选项,则返回一个 `ArrayBuffer`
* 若同时传入了 `stream` 和 `asUint8Array`,则返回 `Uint8Array`
* 否则,返回自上次刷新以来写入的字节数
*
* 该 API 以后可能拆分为 Uint8ArraySink 和 ArrayBufferSink
*/
flush(): number | Uint8Array<ArrayBuffer> | ArrayBuffer;
end(): ArrayBuffer | Uint8Array<ArrayBuffer>;
}