浅析Node.js的Stream模块中的Readable对象


Posted in Javascript onJuly 29, 2015

我一直都很不愿意扯 nodejs 的流,因为从第一次看到它我就觉得它的设计实在是太恶心了。但是没办法,Stream 规范尚未普及,而且确实有很多东西都依赖了 nodejs 的流来实现的,所以我也只能捏着鼻子硬着头皮来扯一扯这又臭又硬的 nodejs 流对象了。

nodejs 自带了一个叫 stream 的模块,引入它便可以得到一组流对象构造器。现在我只说最简单的 stream.Readable。

其实用过 nodejs 的几乎都接触过 Readable 的实例,只是平时没太在意而已。一个非常典型的例子,http 模块中我们处理每个请求时都会有 req 和 res 对象,req 其实就是一个 Readable 对象。我们可以在这个 req 上以流的形式读到 HTTP 请求的实体部分。

那么问题来了,为什么 http 模块要在此处以流的方式设计呢?或者从另一个维度来问这个问题就是「nodejs 如果获取 POST 请求的内容?」。懂得用搜索引擎的同学肯定可以很容易地找到这么一个答案:监听 data 事件收集数据,在 end 事件中把收集到的数据合并起来。是的,这是解决这个问题的方法。但是为什么它如此设计呢?像 PHP 那样直接就可以取到 POST 内容多好?其实这么设计是有好处的,如果我们接收到的数据是非法的,我可以马上察觉,然后响应并断开连接。这样可以避免一些不必要的传输成本。比如上传图片,也许用户错误地选择了一个很大的可执行文件,我们不需要等到这个文件完全上传完毕,只要一个文件头部的若干字节就能判断一个文件是否是图片了。此处使用流的设计就可以先读出前面的几个字节来使用。

上面提到的 data 事件和 end 事件都是 Readable 的事件,这两个事件分别表示收到数据和数据接收完毕。所以其实我们早已知道了 Readable 的用法,只是很多人不知道它是 Readable 对象而已。

但是上面这两个事件仅仅是对 Readable 的消费者而言的事件。内部是如何把一个数据推送到 Readable 对象里面让 Readable 触发出这些事件的呢?那么它就是 push 方法。下面是一个例子,它创建了一个 Readable 对象,这个对象会流出一个递增的数字(这里使用了 babel-node)

import stream from 'stream';

var r = new stream.Readable;

r.on('data', data => {
 console.log(data + '');
});

r.on('end', data => {
 console.log('end');
});

r._read = () => {
 // console.log('before read');
};

void function callee(i) {
 if(i < 10) {
  r.push(i + ''); // 只能传入字符串或 Buffre 对象
 } else {
  r.push(null); // 当输入一个 null 时表示流传输完成,触发 end 事件 
 }
 setTimeout(callee, 500, i + 1);
}(0);

如果仔细看上面代码就会发现一个很神奇的地方,这个代码覆写了 _read 方法,这是什么鬼?其实我也觉得这是个坑,这个私有命名风格就不吐槽了,为何非要覆写这个方法才算实现它?如果没有覆写这个方法,那么在调用 push 时将抛出异常:

Error: not implemented
  at Readable._read (_stream_readable.js:464:22)
  at Readable.read (_stream_readable.js:341:10)

以上这些便是 Readable 对象的基本用法。但是还有更多坑会踩到,这篇文章只是一个最简单的介绍,让大家学会如何造出一个能输出数据的 Readable 对象而已。至于一些 read 之类的基本方法,反正这些也是不科学的设计之一。

Javascript 相关文章推荐
Ucren Virtual Desktop V2.0
Nov 07 Javascript
js中的值类型和引用类型小结 文字说明与实例
Dec 12 Javascript
JQuery实现表格中相同单元格合并示例代码
Jun 26 Javascript
使用GruntJS链接与压缩多个JavaScript文件过程详解
Aug 02 Javascript
jquery delay()介绍及使用指南
Sep 02 Javascript
JS实现密码框根据焦点的获取与失去控制文字的消失与显示效果
Nov 26 Javascript
Boostrap入门准备之border box
May 09 Javascript
Vue.js每天必学之方法与事件处理器
Sep 06 Javascript
Javascript中关于Array.filter()的妙用详解
Dec 04 Javascript
javascript图片预览和上传(兼容IE)
Mar 15 Javascript
VUE写一个简单的表格实例
Aug 06 Javascript
简单聊聊Vue中的计算属性和属性侦听
Oct 05 Vue.js
浅谈Javascript数组的使用
Jul 29 #Javascript
举例讲解Node.js中的Writable对象
Jul 29 #Javascript
浅谈Javascript数组索引
Jul 29 #Javascript
JQ实现新浪游戏首页幻灯片
Jul 29 #Javascript
JavaScript中几种排序算法的简单实现
Jul 29 #Javascript
详解JavaScript中数组的相关知识
Jul 29 #Javascript
javascript+canvas实现刮刮卡抽奖效果
Jul 29 #Javascript
You might like
php中常用的预定义变量小结
2012/05/09 PHP
解析在PHP中使用mysqli扩展库对mysql的操作
2013/07/03 PHP
typecho插件编写教程(四):插件挂载
2015/05/28 PHP
PHP递归实现文件夹的复制、删除、查看大小操作示例
2017/08/11 PHP
lnmp安装多版本PHP共存的方法详解
2018/08/02 PHP
javascript parseInt 函数分析(转)
2009/03/21 Javascript
用户注册常用javascript代码
2009/08/29 Javascript
js实时监听文本框状态的方法
2011/04/26 Javascript
基于NodeJS的前后端分离的思考与实践(五)多终端适配
2014/09/26 NodeJs
EasyUI实现二级页面的内容勾选的方法
2015/03/01 Javascript
简介JavaScript中用于处理正切的Math.tan()方法
2015/06/15 Javascript
基于Jquery插件Uploadify实现实时显示进度条上传图片
2020/03/26 Javascript
jQuery的each循环用法简单示例
2016/06/12 Javascript
基于JavaScript实现点击页面任何位置返回
2016/08/31 Javascript
node.js学习之base64编码解码
2016/10/21 Javascript
详解Nodejs基于mongoose模块的增删改查的操作
2016/12/21 NodeJs
基于jQuery实现咖啡订单管理简单应用
2017/02/10 Javascript
Bootstrap表单控件学习使用
2017/03/07 Javascript
vue单页缓存存在的问题及解决方案(小结)
2018/09/25 Javascript
vue + axios get下载文件功能
2019/09/25 Javascript
JS实现简单的表格增删
2020/01/16 Javascript
[03:36]DOTA2完美大师赛coL战队趣味视频——我演你猜
2017/11/23 DOTA
[00:35]TI7不朽珍藏III——寒冰飞龙不朽展示
2017/07/15 DOTA
python自动格式化json文件的方法
2015/03/11 Python
浅析python中的分片与截断序列
2016/08/09 Python
python实现电脑自动关机
2018/06/20 Python
Python爬虫将爬取的图片写入world文档的方法
2018/11/07 Python
python获取本机所有IP地址的方法
2018/12/26 Python
pycharm激活码有效到2020年11月底
2020/09/18 Python
Django之富文本(获取内容,设置内容方式)
2020/05/21 Python
CSS3中Transition动画属性用法详解
2016/07/04 HTML / CSS
2014年小学植树节活动方案
2014/03/02 职场文书
幼儿园家长寄语
2014/04/02 职场文书
电子装配专业毕业生求职信
2014/04/23 职场文书
2016小学优秀教师先进事迹材料
2016/02/26 职场文书
Spring事务管理下synchronized锁失效问题的解决方法
2022/03/31 Java/Android