Web Streams — это интерфейс, позволяющий работать с потоками данных в браузере и Node.js. Поток (stream) представляет собой последовательность данных, которая может обрабатываться постепенно, без необходимости загружать весь объём сразу. Основные типы потоков в Web Streams: ReadableStream (для чтения данных), WritableStream (для записи данных), TransformStream (для преобразования данных на лету).
Потоки особенно полезны при работе с большими файлами, сетевыми запросами и динамическими данными, позволяя уменьшить нагрузку на память и повысить отзывчивость приложений.
ReadableStream предоставляет интерфейс для чтения данных кусками. Основные компоненты:
Простейший пример создания потока, который выдаёт последовательность чисел:
const stream = new ReadableStream({
start(controller) {
for (let i = 1; i <= 5; i++) {
controller.enqueue(i);
}
controller.close();
}
});
const reader = stream.getReader();
async function read() {
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(value);
}
}
read();
Ключевой момент: enqueue добавляет
данные в поток, close завершает поток, а
getReader() позволяет асинхронно читать данные кусками.
WritableStream используется для записи данных. Основные методы:
Пример записи текста в консоль через поток:
const writable = new WritableStream({
write(chunk) {
console.log('Записано:', chunk);
},
close() {
console.log('Поток завершён');
}
});
const writer = writable.getWriter();
writer.write('Первый фрагмент');
writer.write('Второй фрагмент');
writer.close();
Особенность: getWriter() предоставляет
интерфейс для безопасной записи в поток.
TransformStream объединяет чтение и запись, позволяя преобразовывать данные «на лету». Он содержит:
Пример преобразования текста в верхний регистр:
const upperCaseTransform = new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase());
}
});
const writer = upperCaseTransform.writable.getWriter();
const reader = upperCaseTransform.readable.getReader();
writer.write('тест');
writer.close();
(async () => {
const { value, done } = await reader.read();
if (!done) console.log(value); // 'ТЕСТ'
})();
Важное замечание: TransformStream позволяет создавать цепочки обработки данных, что делает его идеальным для фильтрации, шифрования и сжатия.
Lit (ранее LitElement) интегрируется с Web Streams через реактивные свойства и шаблоны. Потоки позволяют эффективно рендерить данные по мере их поступления.
Пример асинхронного рендеринга списка из потокового источника:
import { LitElement, html, css } from 'lit';
class StreamedList extends LitElement {
static properties = {
items: { type: Array }
};
constructor() {
super();
this.items = [];
this.loadItems();
}
async loadItems() {
const stream = new ReadableStream({
async start(controller) {
for (let i = 1; i <= 5; i++) {
await new Promise(res => setTimeout(res, 500));
controller.enqueue(`Элемент ${i}`);
}
controller.close();
}
});
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
this.items = [...this.items, value];
}
}
render() {
return html`
<ul>
${this.items.map(item => html`<li>${item}</li>`)}
</ul>
`;
}
}
customElements.define('streamed-list', StreamedList);
Ключевой момент: Поток позволяет рендерить элементы постепенно, улучшая пользовательский опыт и снижая нагрузку на DOM.
Fetch API поддерживает потоковое чтение тела ответа через
response.body. Это особенно полезно для больших файлов или
потоковой передачи данных в реальном времени.
const response = await fetch('/large-file.txt');
const reader = response.body.getReader();
let decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(decoder.decode(value));
}
Особенность: использование потоков с
fetch предотвращает загрузку всего файла в память, что
критично при работе с большими объёмами данных.
Web Streams поддерживают соединение нескольких потоков в цепочку:
const source = new ReadableStream({
start(controller) {
controller.enqueue('data1');
controller.enqueue('data2');
controller.close();
}
});
const upperCaseTransform = new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase());
}
});
const destination = new WritableStream({
write(chunk) {
console.log(chunk);
}
});
source.pipeThrough(upperCaseTransform).pipeTo(destination);
Принцип: pipeThrough применяет
преобразование к потоку, pipeTo направляет поток в
WritableStream. Такой подход упрощает обработку данных в несколько
этапов, улучшая читаемость и поддержку кода.
Web Streams идеально сочетаются с реактивными свойствами Lit. Потоки позволяют обновлять состояние компонента постепенно, что делает интерфейс отзывчивым без полной перерисовки.
Использование потоков в Lit повышает производительность при работе с асинхронными источниками данных, такими как API, WebSocket или большие локальные файлы.