Les Streams expliqués pour les développeurs Web
Lorsque vous appelez fetch() et attendez une réponse, le navigateur reçoit déjà ces données par morceaux. L’API Web Streams donne à votre code JavaScript l’accès à ces morceaux au fur et à mesure de leur arrivée, au lieu d’attendre que la réponse complète soit disponible avant de pouvoir la manipuler.
Ce changement — passer de « attendre tout » à « traiter au fur et à mesure » — est l’essence même des streams.
Points clés à retenir
- L’API Web Streams vous permet de traiter les données de manière incrémentale au fur et à mesure de leur arrivée, plutôt que de mettre en mémoire tampon des réponses entières.
ReadableStream,WritableStreametTransformStreamsont les trois primitives fondamentales — des blocs de construction composables pour les pipelines de données.response.bodydefetch()est le point d’entrée le plus courant : unReadableStreamque vous pouvez lire morceau par morceau.- Utilisez
pipeThrough()etpipeTo()pour chaîner les transformations et les sorties ensemble, avec une gestion automatique de la contre-pression intégrée.
Pourquoi tout charger d’un coup pose problème
L’approche traditionnelle pour récupérer des données ressemble à ceci :
const response = await fetch('/large-dataset.json')
const data = await response.json()
// Nothing happens until all bytes are downloaded and parsed
Pour les petites charges utiles, cela convient parfaitement. Pour un fichier JSON de 50 Mo ou une réponse API de longue durée, vous conservez l’intégralité en mémoire avant de traiter un seul enregistrement. Sur des appareils aux ressources limitées ou des connexions lentes, cela se traduit par des interfaces utilisateur lentes, une forte pression mémoire et des utilisateurs frustrés.
Les streams vous permettent de commencer à travailler avec les données dès l’arrivée du premier morceau.
Les trois primitives fondamentales de l’API Web Streams
L’API Web Streams repose sur trois classes :
ReadableStream— une source depuis laquelle vous lisez des donnéesWritableStream— une destination vers laquelle vous écrivez des donnéesTransformStream— se situe au milieu, lisant d’un côté et écrivant des données transformées de l’autre
Les données circulent à travers ces streams sous forme de chunks (morceaux) — de petites portions traitées une à la fois. Un chunk peut être un Uint8Array d’octets, une chaîne de caractères ou n’importe quelle valeur JavaScript, selon le stream.
Streaming avec Fetch : lire une réponse de manière incrémentale
La plupart des réponses fetch() exposent leur corps sous forme de ReadableStream via response.body. C’est le point d’entrée le plus courant dans les streams JavaScript pour les développeurs frontend.
async function processLargeResponse(url) {
const response = await fetch(url)
const reader = response.body.getReader()
const decoder = new TextDecoder()
try {
while (true) {
const { done, value } = await reader.read()
if (done) break
console.log(decoder.decode(value, { stream: true }))
}
} finally {
reader.releaseLock()
}
}
reader.read() retourne une promesse qui se résout avec { value, done }. Lorsque done vaut true, le stream est terminé. Ce pattern vous permet de traiter une réponse de plusieurs mégaoctets morceau par morceau, sans tout mettre en mémoire tampon.
Note sur le streaming des corps de requête : Passer un
ReadableStreamcomme corps de requêtefetch()est possible mais le support navigateur est inégal. Le streaming des réponses est le pattern bien supporté et pratique à privilégier aujourd’hui.
Discover how at OpenReplay.com.
Construire des pipelines de données avec pipeThrough() et pipeTo()
Les streams deviennent véritablement puissants grâce à la composition. Vous pouvez chaîner un ReadableStream à travers une ou plusieurs instances de TransformStream et diriger le résultat vers un WritableStream.
fetch('./data.txt').then((response) =>
response.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase())
}
}))
.pipeTo(new WritableStream({
write(chunk) {
document.body.textContent += chunk
}
}))
)
Ce pipeline décode les octets en texte, transforme chaque chunk en majuscules, puis l’écrit dans le DOM — le tout de manière incrémentale, sans attendre la réponse complète.
pipeThrough() connecte un ReadableStream à un TransformStream et retourne un nouveau ReadableStream. pipeTo() connecte un ReadableStream à un WritableStream et retourne une promesse qui se résout lorsque le stream se termine.
Contre-pression : comment les streams évitent la surcharge
Lorsqu’un consommateur traite les données plus lentement qu’un producteur ne les génère, les streams appliquent une contre-pression (backpressure) — un signal qui se propage en arrière à travers la chaîne de pipes, indiquant à la source de ralentir. Cela se produit automatiquement lorsque vous utilisez pipeTo() et pipeThrough(). C’est l’une des principales raisons de préférer le piping à la lecture manuelle de chunks dans une boucle.
Streams intégrés à connaître
Le navigateur fournit plusieurs utilitaires de stream prêts à l’emploi :
TextDecoderStream/TextEncoderStream— convertissent entre octets et chaînes de caractèresCompressionStream/DecompressionStream— compressent ou décompressent les données à la volée avec gzip ou deflateBlob.stream()— lit n’importe quelBlobouFilecomme unReadableStream
Node.js moderne supporte également l’API Web Streams, donc les pipelines que vous construisez pour le navigateur se transfèrent facilement vers les environnements côté serveur.
Conclusion
L’API Web Streams offre aux développeurs frontend une manière composable et économe en mémoire de gérer les données qui arrivent au fil du temps. ReadableStream et TransformStream sont les primitives que vous utiliserez le plus — surtout combinées avec fetch() pour le traitement incrémental des réponses. Commencez avec response.body, utilisez pipeThrough() lorsque vous devez transformer des données, et laissez la contre-pression gérer le contrôle de flux pour vous.
FAQ
Oui. ReadableStream, WritableStream, TransformStream et les méthodes de piping sont supportés dans tous les navigateurs modernes, y compris Chrome, Firefox, Safari et Edge. Le streaming des corps de réponse fetch via response.body est également largement supporté. Les corps de requête en streaming avec fetch ont un support plus limité, donc vérifiez les tableaux de compatibilité avant de vous appuyer sur cette fonctionnalité.
Si une étape quelconque dans une chaîne de pipes génère une erreur, l'erreur se propage à travers le pipeline. Le côté readable devient en erreur et le côté writable est interrompu. Vous pouvez gérer cela en passant un objet d'options avec un signal ou en capturant la promesse retournée par pipeTo. Pour les boucles de lecture manuelles, enveloppez vos appels de lecture dans des blocs try-catch.
Node.js a initialement fourni sa propre API de streams avec les classes Readable, Writable et Transform. L'API Web Streams est un standard distinct conçu pour les navigateurs. Les versions modernes de Node.js supportent les deux. L'API Web Streams utilise un modèle basé sur le pull avec des promesses, tandis que les streams Node classiques utilisent un modèle push basé sur les événements. Le code écrit avec l'API Web Streams est portable entre les environnements navigateur et serveur.
Si la réponse est petite, disons moins de quelques centaines de kilooctets, la mise en mémoire tampon avec response.json ou response.text est plus simple et parfaitement efficace. Les streams apportent de la valeur lors du traitement de grandes charges utiles, de données en temps réel, ou de situations où vous souhaitez afficher des résultats partiels avant l'arrivée de la réponse complète. Pour des appels API simples retournant du JSON compact, l'approche traditionnelle convient parfaitement.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.