Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як працюють streams (ReadableStream, WritableStream)?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Потік (Stream)** - це послідовність даних, яка надходить поступово, а не вся одразу. **Ключове:** на відміну від звичайного `fetch().text()`, який чекає, поки все прийде, потік (`fetch().body`) дозволяє обробляти дані по шматочках, одразу в міру надходження (chunk by chunk).Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке потік (Stream) > **Потік (Stream)** - це **послідовність даних**, яка надходить **поступово**, > а не вся одразу. Головна відмінність від звичайних даних: - звичайний `fetch().text()` чекає, поки **все прийде** → потім повертає результат; - потік (`fetch().body`) дозволяє **обробляти дані по шматочках**, **одразу в міру надходження** (chunk by chunk). ## Типи потоків у JavaScript | Тип | Призначення | |---|---| | **ReadableStream** | джерело даних (читання) | | **WritableStream** | приймач даних (запис) | | **TransformStream** | проміжна обробка (читання + запис) | | **DuplexStream** | двосторонній (наприклад, сокет) | ## Приклад: читання даних з потоку (ReadableStream) Уяви, ти робиш запит на великий файл: ```javascript const response = await fetch('https://example.com/large-file.txt'); // У response.body - це ReadableStream const reader = response.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; console.log('Отримано chunk розміром:', value.length); } ``` Що відбувається: 1. `fetch` повертає `Response`, у якого `.body` - це **ReadableStream**. 2. Метод `.getReader()` створює **читача**, який повертає дані порціями (chunks). 3. `reader.read()` повертає проміс з `{ done, value }`. - `value` - шматочок даних (тип `Uint8Array`), - `done: true` - потік закінчився. Так можна завантажувати **гігабайти**, не завантажуючи все в оперативну пам'ять. ## Приклад: запис даних у WritableStream ```javascript const stream = new WritableStream({ write(chunk) { console.log('Записуємо chunk:', chunk); }, close() { console.log('Потік завершено'); }, }); const writer = stream.getWriter(); await writer.write('Привіт'); await writer.write('Світ'); await writer.close(); ``` Пояснення: - `WritableStream` приймає дані частинами через `write(chunk)`; - `close()` викликається, коли потік завершено; - можна використовувати для відправки, запису файлів, логування тощо. ## Приклад: TransformStream (перетворення потоку) ```javascript const upperCaseStream = new TransformStream({ transform(chunk, controller) { controller.enqueue(chunk.toUpperCase()); }, }); const writer = upperCaseStream.writable.getWriter(); const reader = upperCaseStream.readable.getReader(); await writer.write('hello '); await writer.write('world!'); await writer.close(); while (true) { const { done, value } = await reader.read(); if (done) break; console.log(value); // "HELLO " "WORLD!" } ``` `TransformStream` з'єднує потік на вході (writable) і на виході (readable), і дозволяє перетворювати дані "на льоту". ## Використання потоків з pipeTo / pipeThrough JS-потоки можна з'єднувати (pipe) як водопровідні труби ```javascript await readableStream .pipeThrough(transformStream) .pipeTo(writableStream); ``` Таким чином: - `pipeThrough` застосовує перетворення; - `pipeTo` передає результат наступному потоку. ## Приклад з fetch() та TextDecoderStream ```javascript const response = await fetch('https://example.com/data.txt'); const textStream = response.body .pipeThrough(new TextDecoderStream()) // декодуємо байти в текст .pipeThrough( new TransformStream({ transform(chunk, controller) { controller.enqueue(chunk.toUpperCase()); // перетворюємо текст }, }) ); const reader = textStream.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; console.log(value); } ``` Тепер кожен шматок файлу перетворюється у **текст**, потім - у **верхній регістр**, і одразу обробляється. ## У Node.js потоки працюють аналогічно У Node.js є своя реалізація (`fs.createReadStream`, `stream.Writable` тощо), але концепція та сама: ```javascript import fs from 'fs'; const readStream = fs.createReadStream('file.txt', { encoding: 'utf-8' }); readStream.on('data', chunk => console.log('Chunk:', chunk)); readStream.on('end', () => console.log('Done')); ``` Потоки Node.js базуються на **EventEmitter**, а браузерні потоки - на **промісах і контролерах**. ## Чому потоки важливі | Перевага | Опис | |---|---| | **Продуктивність** | Немає потреби чекати завантаження всього файлу | | **Економія пам'яті** | Обробка йде "на льоту", шматками | | **Композиція** | Можна об'єднувати потоки в ланцюжки | | **Двосторонній зв'язок** | Використовуються у WebSocket, fetch(), Response, Body | | **Контроль** | Можна призупиняти/відновлювати читання | ## Проста аналогія > Потоки - це як **конвеєр** > > Дані не приходять одразу цілком - > вони "їдуть" по стрічці **порціями (chunks)**, > і на кожному етапі (Readable → Transform → Writable) > ти можеш обробляти, фільтрувати і зберігати їх. ## Підсумок | Тип | Призначення | Приклад використання | |---|---|---| | **ReadableStream** | Джерело даних | `fetch().body`, `file.stream()` | | **WritableStream** | Приймач даних | Запис у файл, лог, мережу | | **TransformStream** | Перетворювач | Стиснення, шифрування, фільтрація | | **pipeTo / pipeThrough** | З'єднання потоків | `readable.pipeThrough(transform).pipeTo(writable)` |Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.