Потоковые ответы (Streaming responses) позволяют получать данные от сервера частями, по мере их готовности, вместо того чтобы ждать завершения всей операции. Это критически важно для работы с большими объёмами данных, интерактивных приложений и реалтайм-интерфейсов. В JavaScript поддержка потоков реализована через Streams API, который является стандартной частью современных браузеров и Node.js.
Streams API делится на несколько ключевых концепций:
Каждый поток состоит из двух основных компонентов: источника (source) и контроллера (controller). Источник отвечает за генерацию данных, контроллер — за управление потоком, его состояние и выдачу чанков (chunks).
Создание потока выглядит следующим образом:
const stream = new ReadableStream({
start(controller) {
controller.enqueue('Hello, ');
controller.enqueue('world!');
controller.close();
}
});
const reader = stream.getReader();
async function readStream() {
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(value);
}
}
readStream();
Объяснение ключевых моментов:
controller.enqueue(value) — добавляет данные в
поток.controller.close() — сигнализирует о завершении
потока.stream.getReader() — получает объект для чтения данных
чанками.reader.read() возвращает объект
{ done, value }. Если поток закончился, done
будет true.Метод fetch в браузерах поддерживает потоковое чтение
ответа через response.body, который является
ReadableStream. Это позволяет обрабатывать данные по
мере их поступления.
Пример чтения JSON-данных частями:
const response = await fetch('https://example.com/large-data.json');
const reader = response.body.getReader();
const decoder = new TextDecoder();
let result = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
result += decoder.decode(value, { stream: true });
}
const data = JSON.parse(result);
console.log(data);
Ключевые моменты:
TextDecoder позволяет декодировать бинарные чанки в
строку.{ stream: true } обеспечивает правильное
объединение чанков.async function streamText(url) {
const response = await fetch(url);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const textChunk = decoder.decode(value, { stream: true });
process.stdout.write(textChunk);
}
}
streamText('https://example.com/streaming-endpoint');
Здесь поток используется для прямого вывода данных на экран без ожидания полного завершения запроса.
TransformStream позволяет модифицировать поток данных на
лету:
const upperCaseStream = new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase());
}
});
const readable = new ReadableStream({
start(controller) {
controller.enqueue('hello ');
controller.enqueue('world');
controller.close();
}
});
const transformed = readable.pipeThrough(upperCaseStream);
const reader = transformed.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(value);
}
Поток сначала создаёт данные, затем через pipeThrough
передаёт их в TransformStream, который модифицирует их.
В Node.js потоки имеют схожую модель, но API отличается:
import fs from 'fs';
const readable = fs.createReadStream('large-file.txt');
readable.on('data', chunk => {
console.log('Получен chunk:', chunk.toString());
});
readable.on('end', () => {
console.log('Поток завершён');
});
Node.js предоставляет события data, end и
error для управления потоками.
Современный подход к потокам — использование асинхронных итераторов:
const response = await fetch('https://example.com/stream');
const decoder = new TextDecoder();
for await (const chunk of response.body) {
console.log(decoder.decode(chunk, { stream: true }));
}
Преимущества:
getReader() и read().locked — показывает, захвачен ли поток
читателем.cancel(reason) — позволяет прекратить
поток преждевременно.pipeTo(destination) — перенаправляет
поток в другой WritableStream.Эти методы позволяют строить сложные конвейеры обработки данных.
Применение потоков особенно важно при работе с:
С потоками данные можно не просто загружать, но и фильтровать, изменять или визуализировать сразу, не дожидаясь завершения всей операции.
Потоковые ответы создают фундамент для современных веб-приложений, позволяя строить быстрые, отзывчивые и ресурсосберегающие системы.