news 2026/8/23 13:35:59

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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-program

Node.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 触发缓存有新数据或流到达尽头时触发

💡 记忆口诀:你只写_readpush,剩下的事件全交给 Node。

3 行核心代码:最小可读流长这样

创建一个可读流的全部"生产逻辑"只有三行(完整代码见 example/principle/_read.js):

const 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,你不需要(也不应该)手动调用它。

流程是这样的:

  1. 下游监听了data事件(或pipe了另一个流)
  2. Node 发现缓存没数据,自动调用你的_read
  3. 你的_readpush入数据
  4. 数据从缓存流出,触发data事件
  5. 缓存又空了 → 回到第 2 步,循环往复

⚠️_read只负责"补水",它不代表"把数据读完"。缓存水位(由highWaterMark控制,默认 16KB)决定每次补多少。相关原理可参考项目中的 docs/highWaterMark.md。

end 事件:最容易踩坑的"结束信号"

end事件 ≠ 调用push(null)的那一刻。它的触发必须同时满足两个条件:

  1. ✅ 已调用push(null),声明不会再有新数据
  2. ✅ 缓存中残留的数据也被全部读取完

看 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()不传参数时会一次性把缓存读空。

新手避坑清单 🛠️

  1. 忘了push(null)→ 下游end永远不触发,流一直挂着
  2. _read里无限同步 push 同一数据→ 缓存无限膨胀,内存爆炸。数据源耗尽后一定要push(null)
  3. 空字符串不是结束→ 非 objectMode 下push('')会被忽略;结束只能用push(null),见 example/principle/push-empty.js
  4. end时机理解错误→ 它发生在"消耗完"而非"产生完",排查流卡住时先确认这个
  5. 在 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)、objectMode(docs/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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/23 13:35:56

德国技术面试中的算法挑战与优化策略

1. 德国技术面试中的算法挑战解析在德国科技企业的技术面试中&#xff0c;算法题向来是考察候选人编程基本功和逻辑思维能力的试金石。最近我参加了几场柏林和慕尼黑知名企业的面试&#xff0c;其中有两道颇具代表性的题目反复出现——经典的"两数之和"问题和一个有趣…

作者头像 李华
网站建设 2026/8/23 13:32:37

亚太杯数学建模竞赛:从高效组队到系统性备赛的实战指南

1. 项目概述&#xff1a;从“组队喊人”到“系统性备赛”的认知跃迁 “2023年亚太杯数学建模竞赛赶紧上车差2个人保2冲1”——这个在各大高校论坛、社群和朋友圈里高频出现的标题&#xff0c;背后远不止是一则简单的组队招募启事。它精准地戳中了数模竞赛参与者的核心痛点&…

作者头像 李华
网站建设 2026/8/23 13:31:12

Visual Studio 2022安装与编译实战:从C/C++到C#的完整环境搭建与避坑指南

1. 项目概述&#xff1a;为什么从安装到编译都值得细说 每次看到有朋友在群里问“Visual Studio 2022怎么装”、“C语言代码怎么跑不起来”&#xff0c;我都会想起自己刚入门时对着报错信息一头雾水的样子。这个标题看起来像是基础教程&#xff0c;但“安装与编译”加上“番外”…

作者头像 李华
网站建设 2026/8/23 13:29:34

Java面试全攻略:从JVM调优到秒杀系统设计

1. 面试背景与个人准备 作为非科班出身的开发者&#xff0c;我花了三个月系统性复习Java核心知识。从JVM内存模型到并发编程工具类&#xff0c;每天保持6小时高强度刷题。特别针对大厂常考的算法题型&#xff0c;用Excel做了错误率统计表&#xff0c;反复打磨薄弱环节。 简历重…

作者头像 李华
网站建设 2026/8/23 13:28:34

SG-Exam:3 步搭起你的在线考试平台

SG-Exam&#xff1a;3 步搭起你的在线考试平台 【免费下载链接】sg-exam 基于Spring Boot搭建的新一代教学管理平台&#xff0c;提供权限管理、考试、问卷、练习等功能 项目地址: https://gitcode.com/gh_mirrors/sg/sg-exam SG-Exam 是一个基于 Spring Boot Vue 的开源…

作者头像 李华
网站建设 2026/8/23 13:22:58

数学建模竞赛实战:微分方程与优化算法在生态经济系统中的应用

1. 项目概述&#xff1a;从赛题到现实问题的映射每年九月的那个周末&#xff0c;对于国内理工科研究生来说&#xff0c;都是一场脑力与耐力的双重考验——中国研究生数学建模竞赛。2022年的E题“草原放牧策略研究”&#xff0c;乍一看似乎离我们熟悉的代码、算法有些距离&#…

作者头像 李华