Веб-потоки (web streams) предоставляют основанный на веб-стандартах способ асинхронной потоковой передачи данных по сети. Они позволяют разработчикам обрабатывать большие наборы данных по чанкам (chunks — части, куски), контролировать перегрузку сети (обратное давление — backpressure) и создавать высокоэффективные и отзывчивые приложения. Использование веб-потоков может повысить производительность и отзывчивость приложения. Обработка данных по мере их поступления позволяет применять обновления и реагировать на взаимодействия пользователя в реальном времени, обеспечивая бесшовный пользовательский опыт за счет быстрой загрузки данных, предоставления актуальной информации и плавного, более интерактивного интерфейса. Web Streams API постепенно становится краеугольным камнем основных веб-платформ, включая браузеры, Node.js и Deno. В этой статье мы рассмотрим, что такое веб-потоки, как они работают, их преимущества, а также инструменты, созданные на их основе. 
 Двумя основными преимуществами веб-потоков являются: Веб-потоки могут быть разделены на 3 основных типа: WritableStream, ReadableStream и TransformStream. У каждого из них своя роль: Веб-потоки могут объединяться в цепочки (chain) — последовательность шагов по обработке данных, что повышает читаемость и "поддерживаемость" кода, а также облегчает создание сложных конвейеров (pipeline) обработки данных. 
 Чанки — это фундаментальные единицы данных в веб-потоках, часто представленные в виде строк (текстовые потоки) или Uint8Array (бинарные потоки). Чанки могут иметь разные формы и размеры, что зависит от таких факторов, как: Наш код должен быть готов к обработке чанков любого размера, поскольку его сложно предсказать, и мы его часто не контролируем. Рассмотрим пример. Сначала мы кодируем (encode) строку "Stream me!" и помещаем ее в очередь (enqueue) в ReadableStream. Этот ReadableStream становится источником данных, который могут потреблять (consume) другие потоки. Для передачи данных от ReadableStream другому потоку используется метод pipeThrough. Данные передаются TransformStream, который получает чанки данных, декодирует (decode) их в строки текста, приводит строку к верхнему регистру, кодирует ее и помещает преобразованный чанк для потребления следующим потоком. Данные передаются WritableStream с помощью метода pipeTo. По сути, WritableStream — это конечная точка (endpoint), позволяющая потребить данные кастомизированным способом. В данном случае мы декодируем чанк данных и выводим его в консоль. Одним из основных способов взаимодействия с веб-потоками является метод getReader, предоставляемый Fetch API. Данный метод позволяет последовательно читать чанки данных из тела запроса по мере их прибытия, что позволяет эффективно обрабатывать большое количество данных. Одной из самых мощных возможностей, предоставляемых веб-потоками, является встроенная обработка обратного давления, которое происходит, когда данные генерируются быстрее, чем потребляются, например, когда высокоскоростной сервер посылает данные клиенту с медленным соединением. При несовпадении скорости генерации и обработки данных, лишние элементы помещаются в очередь и ждут обработки потребителем. Если это продолжается долгое время, очередь продолжает расти, что может привести к заполнению памяти со всеми вытекающими последствиями. 
 Обработка обратного давления может быть сложной из-за необходимости соблюдения баланса между скоростью генерации и потребления данных. Бесконтрольная генерация данных может привести к проблемам с памятью из-за буферизации данных. С другой стороны приостановка генерации данных может привести к простою их генератора при наличии доступных возможностей по обработке данных. Веб-потоки обрабатывают обратное давление через управление потоком данных (flow control). Когда поток находится в состоянии readable, данные свободно передаются между генератором и потребителем. Если входящие данные начинают превышать возможности потребителя, поток переходит в состояние backpressure. Это состояние является сигналом для генератора о необходимости приостановки производства данных. Когда потребитель освобождается, поток снова переходит в состояние readable, и генерация данных продолжается. Этот автоматический механизм защищает потребителя от переполнения данными, а генератора — от простоя. 
 В ReadableStream обратное давление применяется косвенно через читателя. Когда читатель извлекает (pull) чанки из потока, это сигнализирует о его готовности к обработке новых данных. Если читатель перестал извлекать данные (метод read перестал вызываться), поток делает вывод о том, что читатель занят обработкой имеющихся данных, и генерация новых данных должна быть приостановлена. В WritableStream обратное давления применяется напрямую к писателю. Метод write возвращает промис, который разрешается (resolve) только когда поток готов к новым данным. Таким образом, неразрешенный промис служит индикатором обратного давления. Использование ключевого слова await приводит к приостановке выполнения кода до разрешения промиса. Это гарантирует, что мы не пишем данные быстрее, чем поток для записи может их обработать. Server-sent events (SSE), что условно можно перевести как "события, отправляемые сервером", являются популярным способом доставки обновлений от сервера к клиенту в реальном времени. В то время как веб-потоки используются, в основном, для обработки данных, SSE поддерживает открытое соединение с сервером, позволяя передавать данные по мере их появления. 
 Веб-потоки закрывают соединение после передачи всех данных. SSE используют долгоживущее (long-lived) соединение HTTP, которое может использоваться сервером для передачи новых данных. SSE может быть востребован в приложениях, где новые данные генерируются в реальном времени, включая провайдеров ИИ (искусственный интеллект — artificial intelligence, AI), таких как OpenAI. Для обработки ответа SSE, содержащего обычный текст (plain text), можно применить библиотеку eventsource-parser для разбора фрагментированных чанков, передаваемых с помощью функции feed: Полный пример можно найти в этом репозитории. Прим. пер.: далее речь идет об основанных на веб-потоках возможностях, предоставляемых Vercel. Если вы не планируете пользоваться этой платформой, можете закончить чтение статьи на этом месте. Vercel поддерживает веб-потоки как в граничной (edge), так и в бессерверной (serverless) средах выполнения, что позволяет эффективно обрабатывать данные в реальном времени. Это позволяет "стримить" данные в формате JSON клиенту или даже прогрессивно рендерить части UI (user interface — пользовательский интерфейс). Прогрессивный рендеринг может быть использован для улучшения опыта пользователей с медленным соединением. Тот же принцип применим в отношении данных JSON, когда мы хотим отправлять части большого набора данных по мере их готовности. Построение UI вокруг данных LLM (large language model — большая языковая модель) стало очень популярным на фоне роста популярности провайдеров ИИ. Стриминг ответов LLM привел к важным изменениям в подходах к разработке приложений. Решение об использовании потоковой передачи данных зависит от таких факторов, как размер языковой модели, скорость и длина ответа. Большие модели могут генерировать сложные ответы, но медленно отвечать из-за сложных вычислений. Маленькие модели отвечают быстро, но их ответы гораздо проще, чего может быть недостаточно для удовлетворения потребностей приложения. Vercel AI SDK помогает минимизировать количество шаблонного кода, необходимого для обработки потоковых ответов, облегчая получение и рендеринг таких ответов: Эти потоковые ответы могут потребляться хуками useChat и useCompletion: Веб-потоки сильно меняют подход к обработке данных в веб-приложениях. Они предоставляют мощный, эффективный и стандартизированный способ асинхронной обработки большого количества данных. Возможности автоматической обработки обратного давления и доставки данных по частям могут существенно повысить производительности приложения. Преобразование данных в процессе передачи открывает целый спектр возможностей по обработке и манипуляции данными. JavaScript Developer