الـ Streams هي تجريد مهم للعمل مع البيانات الثنائية بدون تحميلها كلها في الذاكرة دفعة واحدة. تُستخدم عادةً لقراءة وكتابة الملفات، وإرسال واستقبال طلبات الشبكة، ومعالجة كميات كبيرة من البيانات.
يطبق Bun واجهات برمجة التطبيقات الويب ReadableStream و WritableStream.
NOTE
يطبق Bun أيضًا وحدة `node:stream`، بما في ذلك [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams)، [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams)، و [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). للحصول على الوثائق الكاملة، راجع [وثائق Node.js](https://nodejs.org/api/stream.html).لإنشاء ReadableStream بسيط:
const stream = new ReadableStream({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});يمكن قراءة محتويات ReadableStream قطعة قطعة باستخدام صيغة for await.
for await (const chunk of stream) {
console.log(chunk);
}
// hello
// worldReadableStream المباشر
يطبق Bun نسخة محسنة من ReadableStream تتجنب نسخ البيانات غير الضروري ومنطق إدارة الطابور.
مع ReadableStream التقليدي، يتم إدراج قطع البيانات في طابور. كل قطعة تُنسخ إلى طابور، حيث تبقى حتى يكون الـ stream جاهزًا لإرسال المزيد من البيانات.
const stream = new ReadableStream({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});مع ReadableStream المباشر، تُكتب قطع البيانات مباشرة إلى الـ stream. لا يحدث طابور، ولا حاجة لاستنساخ بيانات القطعة إلى الذاكرة. تم تحديث واجهة برمجة التطبيقات controller لتعكس ذلك؛ بدلاً من .enqueue() تستدعي .write.
const stream = new ReadableStream({
type: "direct",
pull(controller) {
controller.write("hello");
controller.write("world");
},
});عند استخدام ReadableStream مباشر، يتم التعامل مع جميع قطع الطابور بواسطة الوجهة. يتلقى مستهلك الـ stream بالضبط ما يتم تمريره إلى controller.write()، بدون أي ترميز أو تعديل.
Streams المولدات غير المتزامنة
يدعم 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"إذا كنت بحاجة إلى تحكم أكثر دقة في الـ stream، سيعيد yield controller الـ 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، مرر خيار asUint8Array إلى طريقة start.
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() تدعم السلاسل، المصفوفات المtyped، ArrayBuffer، و SharedArrayBuffer.
sink.write("h");
sink.write(new Uint8Array([101, 108]));
sink.write(Buffer.from("lo").buffer);
sink.end();بمجرد استدعاء .end()، لا يمكن كتابة المزيد من البيانات إلى ArrayBufferSink. ومع ذلك، في سياق تخزين stream مؤقتًا، من المفيد كتابة البيانات باستمرار و.flush() المحتويات بشكل دوري (على سبيل المثال، في WriteableStream). لدعم ذلك، مرر stream: true إلى المنشئ.
const sink = new Bun.ArrayBufferSink();
sink.start({
stream: true,
});
sink.write("h");
sink.write("e");
sink.write("l");
sink.flush();
// ArrayBuffer(5) [ 104, 101, 108 ]
sink.write("l");
sink.write("o");
sink.flush();
// ArrayBuffer(5) [ 108, 111 ]طريقة .flush() ترجع البيانات المخزنة مؤقتًا كـ ArrayBuffer (أو Uint8Array إذا كان asUint8Array: true) وتمسح المخزن المؤقت الداخلي.
لتعيين حجم المخزن المؤقت الداخلي يدويًا بالبايت، مرر قيمة لـ highWaterMark:
const sink = new Bun.ArrayBufferSink();
sink.start({
highWaterMark: 1024 * 1024, // 1 MB
});المرجع
/**
* كاتب تزايدي سريع يصبح `ArrayBuffer` عند end().
*/
export class ArrayBufferSink {
constructor();
start(options?: {
asUint8Array?: boolean;
/**
* تخصيص مخزن مؤقت داخلي بهذا الحجم
* هذا يمكن أن يحسن الأداء بشكل كبير عندما يكون حجم القطعة صغيرًا
*/
highWaterMark?: number;
/**
* عند {@link ArrayBufferSink.flush}، ترجع البيانات المكتوبة كـ `Uint8Array`.
* ستبدأ الكتابات من بداية المخزن المؤقت.
*/
stream?: boolean;
}): void;
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
/**
* تفريغ المخزن المؤقت الداخلي
*
* إذا تم تمرير خيار `stream` إلى {@link ArrayBufferSink.start}، سيرجع هذا `ArrayBuffer`
* إذا تم تمرير خيار `stream` و `asUint8Array` إلى {@link ArrayBufferSink.start}، سيرجع هذا `Uint8Array`
* وإلا، سيرجع عدد البايتات المكتوبة منذ آخر تفريغ
*
* قد تتغير هذه الواجهة لاحقًا لفصل Uint8ArraySink و ArrayBufferSink
*/
flush(): number | Uint8Array<ArrayBuffer> | ArrayBuffer;
end(): ArrayBuffer | Uint8Array<ArrayBuffer>;
}