Les streams sont une abstraction importante pour travailler avec des données binaires sans tout charger en mémoire d'un coup. Ils sont couramment utilisés pour la lecture et l'écriture de fichiers, l'envoi et la réception de requêtes réseau, et le traitement de grandes quantités de données.
Bun implémente les API Web ReadableStream et WritableStream.
NOTE
Bun implémente également le module `node:stream`, y compris [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams), [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), et [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). Pour une documentation complète, consultez la [documentation Node.js](https://nodejs.org/api/stream.html).Pour créer un simple ReadableStream :
const stream = new ReadableStream({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});Le contenu d'un ReadableStream peut être lu morceau par morceau avec la syntaxe for await.
for await (const chunk of stream) {
console.log(chunk);
}
// hello
// worldReadableStream direct
Bun implémente une version optimisée de ReadableStream qui évite la copie inutile de données et la logique de gestion de file d'attente.
Avec un ReadableStream traditionnel, les morceaux de données sont mis en file d'attente. Chaque morceau est copié dans une file d'attente, où il reste jusqu'à ce que le stream soit prêt à envoyer plus de données.
const stream = new ReadableStream({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});Avec un ReadableStream direct, les morceaux de données sont écrits directement dans le stream. Aucune mise en file d'attente ne se produit, et il n'est pas nécessaire de cloner les données du morceau en mémoire. L'API controller est mise à jour pour refléter cela ; au lieu de .enqueue() vous appelez .write.
const stream = new ReadableStream({
type: "direct",
pull(controller) {
controller.write("hello");
controller.write("world");
},
});Lors de l'utilisation d'un ReadableStream direct, toute la mise en file d'attente des morceaux est gérée par la destination. Le consommateur du stream reçoit exactement ce qui est passé à controller.write(), sans aucun encodage ou modification.
Streams générateurs asynchrones
Bun prend également en charge les fonctions génératrices asynchrones comme source pour Response et Request. C'est un moyen facile de créer un ReadableStream qui récupère des données depuis une source asynchrone.
const response = new Response(
(async function* () {
yield "hello";
yield "world";
})(),
);
await response.text(); // "helloworld"Vous pouvez également utiliser [Symbol.asyncIterator] directement.
const response = new Response({
[Symbol.asyncIterator]: async function* () {
yield "hello";
yield "world";
},
});
await response.text(); // "helloworld"Si vous avez besoin d'un contrôle plus granulaire sur le stream, yield renverra le contrôleur ReadableStream direct.
const response = new Response({
[Symbol.asyncIterator]: async function* () {
const controller = yield "hello";
await controller.end();
},
});
await response.text(); // "hello"Bun.ArrayBufferSink
La classe Bun.ArrayBufferSink est un écrivain incrémentiel rapide pour construire un ArrayBuffer de taille inconnue.
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 ]Pour récupérer les données sous forme de Uint8Array, passez l'option asUint8Array à la méthode 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 ]La méthode .write() prend en charge les chaînes, les tableaux typés, ArrayBuffer et SharedArrayBuffer.
sink.write("h");
sink.write(new Uint8Array([101, 108]));
sink.write(Buffer.from("lo").buffer);
sink.end();Une fois que .end() est appelé, aucune donnée supplémentaire ne peut être écrite dans l'ArrayBufferSink. Cependant, dans le contexte de la mise en tampon d'un stream, il est utile d'écrire continuellement des données et de .flush() périodiquement le contenu (par exemple, dans un WriteableStream). Pour prendre en charge cela, passez stream: true au constructeur.
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 ]La méthode .flush() renvoie les données mises en tampon sous forme d'ArrayBuffer (ou Uint8Array si asUint8Array: true) et vide le tampon interne.
Pour définir manuellement la taille du tampon interne en octets, passez une valeur pour highWaterMark :
const sink = new Bun.ArrayBufferSink();
sink.start({
highWaterMark: 1024 * 1024, // 1 Mo
});Référence
/**
* Écrivain incrémentiel rapide qui devient un `ArrayBuffer` lors de end().
*/
export class ArrayBufferSink {
constructor();
start(options?: {
asUint8Array?: boolean;
/**
* Préallouer un tampon interne de cette taille
* Cela peut améliorer considérablement les performances lorsque la taille du morceau est petite
*/
highWaterMark?: number;
/**
* Lors de {@link ArrayBufferSink.flush}, renvoie les données écrites sous forme de `Uint8Array`.
* Les écritures redémarreront depuis le début du tampon.
*/
stream?: boolean;
}): void;
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
/**
* Vider le tampon interne
*
* Si {@link ArrayBufferSink.start} a reçu une option `stream`, cela renverra un `ArrayBuffer`
* Si {@link ArrayBufferSink.start} a reçu une option `stream` et `asUint8Array`, cela renverra un `Uint8Array`
* Sinon, cela renverra le nombre d'octets écrits depuis le dernier flush
*
* Cette API pourrait changer plus tard pour séparer Uint8ArraySink et ArrayBufferSink
*/
flush(): number | Uint8Array<ArrayBuffer> | ArrayBuffer;
end(): ArrayBuffer | Uint8Array<ArrayBuffer>;
}