Web Streams 的隐性开销与JavaScript 流处理新方案

Web Streams 的隐性开销与JavaScript 流处理新方案

处理视频流时突然卡顿?处理大文件时内存爆表?这些看似奇怪的问题,可能源于 JavaScript 中一个被广泛采用但设计复杂的标准 API——Web Streams。当你的 Node.js 应用突然因为未消费的 body 耗尽连接池,或者处理大文件时内存爆表,你可能已经踩过 Web Streams 的坑了。

问题:Web Streams 的设计缺陷

Web Streams 是 JavaScript 中处理数据流的标准 API,2014-2016 年设计,旨在统一浏览器和服务器的数据流处理。它被用于 fetch()、Node.js、Cloudflare Workers 等场景,成为现代 Web 应用的数据传输基础。WHATWG Streams Standard 文档 定义了这套机制,初衷是让开发者能以统一方式处理实时数据、大文件、网络请求等流式场景。然而,经过多年的实际使用,开发者们发现这个标准 API 存在诸多问题。

例如,读取流数据需要繁琐的锁管理:const reader = stream.getReader();reader.releaseLock();,一旦忘记释放锁,整个流就永久锁死。更严重的是,response.clone() 这样的 API 会隐式创建分支流,如果不消费所有分支,会导致连接池耗尽。Matteo Collina(Node.js 技术委员会主席)在讨论中指出:「Cloning streams in Node.js’s fetch() implementation is harder than it looks… the coordination required between two readers sharing one source makes it easy to accidentally break the original request or exhaust connection pools.」(在 Node.js 的 fetch() 实现中克隆流比看起来困难得多……共享单个源的两个读取器之间需要协调,这很容易意外破坏原始请求或耗尽连接池。)

BYOB(自带缓冲区)机制本为优化内存,但实际使用复杂:需要单独的 ReadableStreamBYOBReader,处理缓冲区转移,且几乎不被使用。背压机制也形同虚设——desiredSize 只是建议值,生产者可以无视地持续写入,导致内存无限增长。Vercel 的 研究 显示,Node.js 中 Web Streams 管道性能比优化后方案低 12 倍,主要问题在于「Promise 和对象分配开销」。

新方案:原生异步迭代流

一位 Cloudflare 工程师提出新方案:将流设计为原生 async iterable,直接通过 for await...of 消费,无需锁管理。数据仅在消费时处理(pull-through),批量处理 Uint8Array[] 减少 Promise 开销,同步/异步分离路径避免无谓开销。例如,创建和消费流的代码从:

const{ readable, writable }=newTransformStream();const enc =newTextEncoder();const writer = writable.getWriter();await writer.write(enc.encode("Hello, World!"));await writer.close(); writer.releaseLock();const dec =newTextDecoder();let text ='';forawait(const chunk of readable){ text += dec.decode(chunk,{stream:true});} text += dec.decode();

简化为:

import{ Stream }from'new-streams';const{ writer, readable }= Stream.push();await writer.write("Hello, World!");await writer.end();const text =await Stream.text(readable);

性能测试显示,新方案比 Web Streams 快 2-120 倍。例如,链式 3 次转换场景提升 80-90 倍,async iteration 快 40-100 倍。这是因为避免了中间缓冲区、减少了 Promise 创建,且同步场景可以完全跳过异步开销。一位 Node.js 核心贡献者评价:「We’ve done a lot to improve performance and consistency in Node streams, but there’s something uniquely powerful about starting from scratch. New streams’ approach embraces modern runtime realities without legacy baggage…」(我们在改进 Node 流的性能和一致性方面做了很多工作,但从零开始设计的流方案有种独特的力量,它拥抱现代运行时特性而没有历史包袱……)

开发者社区的争议与思考

Hacker News 上,开发者们对新方案有不同看法。有人质疑「每字节创建对象」的方案会导致 GC 压力,但支持者认为 JS 引擎可优化短生命周期对象。关于同步/异步分离,有人认为「应该统一 API 避免代码重复」,但也有开发者分享实际案例:Lit-SSR 通过「同步迭代器+thunk」实现 12-18 倍 SSR 性能提升。一位开发者指出:「The tension between ‘streams as lazy sequences’ vs ‘streams as async event channels’ isn’t unique to JavaScript. Every major runtime has hit this wall… .NET actually handled this better with IAsyncEnumerable in C# 8 — a single abstraction that’s pull-based but async-aware.」(「流作为惰性序列」和「流作为异步事件通道」之间的张力并非 JavaScript 独有,所有主流运行时都遇到过这个难题…….NET 通过 C# 8 的 IAsyncEnumerable 单一抽象(基于拉取且支持异步)实际上处理得更好。)

这不仅是技术改进,更是对开发体验的重视。当 API 设计回归简单、显式、高性能的原生迭代模式,开发者才能真正专注于业务逻辑,而不是与复杂 API「战斗」。正如一位开发者所说:「We deserve a better stream API. So let’s talk about what that could look like.」(我们值得拥有更好的流 API。那我们就来探讨下它可能的模样吧。)

在这里插入图片描述

Read more

数据结构 | 深度解析二叉树的基本原理

数据结构 | 深度解析二叉树的基本原理

个人主页-爱因斯晨 文章专栏-数据结构 二叉树是计算机科学中最基础也最常用的数据结构之一,它不仅是理解更复杂树结构(如 AVL 树、红黑树)的基础,也广泛应用于表达式解析、 Huffman 编码、数据库索引等领域。本文将从二叉树的基本概念出发,深入探讨其存储结构、核心操作及实际应用,并通过 C 语言代码示例帮助读者掌握这一重要数据结构。 二叉树的基本概念 二叉树是一种每个节点最多有两个子节点的树状结构,这两个子节点分别被称为左孩子(left child)和右孩子(right child)。根据节点的分布情况,二叉树可以分为以下几种特殊类型: * 满二叉树:除叶子节点外,每个节点都有两个子节点,且所有叶子节点都在同一层 * 完全二叉树:除最后一层外,其余层都是满的,且最后一层的节点都靠左排列 * 平衡二叉树:左右两个子树的高度差不超过 1 的二叉搜索树 二叉树具有一个重要性质:在非空二叉树中,第 i 层最多有 2^(i-1) 个节点;深度为

By Ne0inhk

矩形树图Treemap布局算法深度解析:基于Highcharts实现带层级交互的矩形树图

Treemap |矩形树图 需求 该树状图需要文件modules/treemap.js。 若要与 colorAxis一起使用,还必须包含 modules/heatmap.js。 数据结构 数据的结构是一个 树tree,每个点代表一个节点。每个 节点都可以有自己的子节点。 树会自动在顶部创建一个节点,代表根节点。如果某个点没有定义父节点,或者父节点的ID不存在,父节点将会自动设置为根节点。 以下是Highcharts中树的构建示例: data:[{name:'I have children',id:'id-1'},{name:'I am a child',parent:'id-1',value:2},{name:'

By Ne0inhk

Clawdbot整合Qwen3:32B效果展示:Qwen3:32B在中文编程题解生成、复杂算法说明、LeetCode模拟面试表现

Clawdbot整合Qwen3:32B效果展示:Qwen3:32B在中文编程题解生成、复杂算法说明、LeetCode模拟面试表现 1. Clawdbot是什么:一个让AI代理管理变简单的平台 Clawdbot不是另一个需要从零搭建的AI服务,而是一个开箱即用的AI代理网关与管理平台。它像一个智能调度中心,把多个大模型能力统一接入、集中管理、直观操作。对开发者来说,不用再为每个模型单独写接口、配参数、调超参——你只需要关注“我要让AI做什么”。 它的核心价值很实在: * 一个聊天界面就能和不同模型对话,不用切换网页或工具 * 支持多模型并行接入(比如同时挂载Qwen3、Llama3、DeepSeek等) * 所有代理的运行状态、响应耗时、错误日志一目了然 * 通过简单配置就能扩展新功能,比如加个代码执行沙箱、接个数据库查询插件 特别适合两类人: 一是正在做AI应用原型验证的工程师,想快速对比不同模型在具体任务上的表现; 二是技术团队负责人,需要统一管控模型调用权限、成本和稳定性,而不是放任每个成员各自部署一堆本地Ollama实例。 这次我们重点测试的是Clawdbo

By Ne0inhk
《算法题讲解指南:优选算法-双指针》--03快乐数,04盛水最多的容器

《算法题讲解指南:优选算法-双指针》--03快乐数,04盛水最多的容器

🔥小叶-duck:个人主页 ❄️个人专栏:《Data-Structure-Learning》 《C++入门到进阶&自我学习过程记录》《算法题讲解指南》--从优选到贪心 ✨未择之路,不须回头 已择之路,纵是荆棘遍野,亦作花海遨游 目录 03.快乐数 题目链接: 题目描述: 题目示例: 题目分析: 算法思路: C++代码演示: 算法总结及流程解析: 04.盛水最多的容器 题目链接: 题目描述: 题目示例: 算法思路: C++代码演示: 算法总结及流程解析: 结束语 03.快乐数 题目链接: 202. 快乐数 - 力扣(LeetCode) 题目描述: 题目示例: 题目分析:       为了方便叙述,将【对于一个正整数,

By Ne0inhk