Node.js Readable可读流完全指南:streamify-your-node-program教你用3行代码读懂_read、push与end事件

发布时间:2026/8/23 13:36:10
Node.js Readable可读流完全指南:streamify-your-node-program教你用3行代码读懂_read、push与end事件 Node.js Readable可读流完全指南streamify-your-node-program教你用3行代码读懂_read、push与end事件【免费下载链接】streamify-your-node-program对Node.js中 stream模块的学习积累和理解项目地址: https://gitcode.com/gh_mirrors/st/streamify-your-node-programNode.js Readable 可读流完全指南streamify-your-node-program 教你用 3 行代码读懂 _read、push 与 end 事件如果你正在学习Node.js 流Stream大概率卡在 Readable 可读流的三个概念上_read方法、push方法和end事件。本文基于开源项目streamify-your-node-program一个专注 Node.js stream 模块学习积累的教程项目的文档与示例用最小代码带你一次读懂可读流的工作原理——从数据怎么泵出来到流如何正确结束。什么是 Readable 可读流一句话定义Readable 可读流作为上游负责产生数据并推送给下游典型代表就是fs.createReadStream读文件流。把它想象成一根水管push()—— 往水管里灌水_read()—— 水泵的开关下游需要水时自动开启push(null)—— 宣布水源枯竭水管不会再有水了end事件 —— 下游把最后一滴水也取走时的信号项目中的 docs/readable.md 对这部分有完整讲解配合 example/principle/ 目录下的十余个最小示例是理解可读流原理的绝佳材料。核心 API 速览谁是谁的水泵API谁来写作用_read()你数据产生的入口Node 在需要时自动调用它push(chunk)你把数据送入可读流内部缓存push(null)你声明数据已全部产生结束流data事件Node 触发flowing 模式下每收到一块数据触发一次end事件Node 触发数据被完全消耗时触发readable事件Node 触发缓存有新数据或流到达尽头时触发 记忆口诀你只写_read和push剩下的事件全交给 Node。3 行核心代码最小可读流长这样创建一个可读流的全部生产逻辑只有三行完整代码见 example/principle/_read.jsconst Readable require(stream).Readable const dataSource [a, b, c] const readable Readable() readable._read function () { if (dataSource.length) { this.push(dataSource.shift()) // 第 1 步从数据源取一块 } else { this.push(null) // 第 2 步取完了宣告结束 } } readable.on(data, data process.stdout.write(data)) // 第 3 步监听 data 消费数据 // 输出abc这三行就是所有可读流的骨架_read是水泵push是灌水push(null)是关闸。关于 push同步、异步都行push既可以在_read里同步调用也可以异步调用真实场景大多如此比如读文件、请求网络。example/principle/push.js 展示了用process.nextTick异步 push 的写法效果完全一致——数据总是要等下游来拉才会真正流出。_read 是如何被触发的新手最大的疑惑_read什么时候被调用答案是——当下游需要数据、而缓存不足时Node 内部自动调用_read你不需要也不应该手动调用它。流程是这样的下游监听了data事件或pipe了另一个流Node 发现缓存没数据自动调用你的_read你的_read里push入数据数据从缓存流出触发data事件缓存又空了 → 回到第 2 步循环往复⚠️_read只负责补水它不代表把数据读完。缓存水位由highWaterMark控制默认 16KB决定每次补多少。相关原理可参考项目中的 docs/highWaterMark.md。end 事件最容易踩坑的结束信号end事件 ≠ 调用push(null)的那一刻。它的触发必须同时满足两个条件✅ 已调用push(null)声明不会再有新数据✅ 缓存中残留的数据也被全部读取完看 example/event-end.js 的真实输出顺序push a push b data Buffer 61 push c data Buffer 62 push null ← 这里还没 end缓存里还有 c data Buffer 63 end ← 数据真正消耗完end 才触发 这说明end是给下游的可以收尾了信号而忘了调用push(null)的流会让下游永远等待——这是可读流头号 bug。flowing 与 paused两种消费模式flowing 模式默认推荐只要监听了data事件或执行了pipe流就进入 flowing 模式数据会持续不断地自动流出。90% 的场景你都在用它readable.on(data, data console.log(data))完整示例见 example/flowing-mode.js。实际开发中更推荐readable.pipe(writable)的方式消费流它还能自动处理背压详见 docs/pipe.md。paused 模式主动控制⏸️流创建后的初始状态就是 paused 模式。此时数据不会自动流出需要监听readable事件后手动调用read()从缓存取数readable.on(readable, function () { var data while (data this.read()) { console.log(data) } }完整示例见 example/paused-mode.js。注意输出里Buffer 61 62是两块数据被合并读取的结果——read()不传参数时会一次性把缓存读空。新手避坑清单 ️忘了push(null)→ 下游end永远不触发流一直挂着_read里无限同步 push 同一数据→ 缓存无限膨胀内存爆炸。数据源耗尽后一定要push(null)空字符串不是结束→ 非 objectMode 下push()会被忽略结束只能用push(null)见 example/principle/push-empty.jsend时机理解错误→ 它发生在消耗完而非产生完排查流卡住时先确认这个在 flowing 模式下手动 read() 混用→ 会改变流的内部状态非必要别混着来小结一张图读懂可读流生命周期下游监听 data / pipe ↓ Node 自动调用 _read() ↓ push(chunk) 入缓存 ↓ data 事件流出数据 ↓ 数据源耗尽 → push(null) ↓ 缓存读空 → end 事件 核心结论用push产生数据_read是自动触发的水泵必须用push(null)结束流否则下游一直等待end事件 数据被完全消耗完的时刻绝大多数场景使用 flowing 模式 pipe消费即可想继续深入streamify-your-node-program 项目里还有 Writable 可写流docs/writable.md、objectModedocs/objectMode.md、Transform 流docs/duplex-and-transform.md以及 Browserify、Gulp 的流实战docs/gulp.md照着读下来Node.js 流模块就彻底通了。【免费下载链接】streamify-your-node-program对Node.js中 stream模块的学习积累和理解项目地址: https://gitcode.com/gh_mirrors/st/streamify-your-node-program创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考