Node.js中的流(Stream)介绍


Posted in Javascript onMarch 30, 2015

什么是流?

说到流,就涉及到一个*nix的概念:管道——在*nix中,流在Shell中被实现为可以通过 |(管道符) 进行桥接的数据,一个进程的输出(stdout)可被直接作为下一个进程的输入(stdin)。

在Node中,流(Stream)的概念与之类似,代表一种数据流可供桥接的能力。

pipe

流化的精髓在于 .pipe()方法。可供桥接的能力,在于数据流的两端(上游/下游 或称为 读/写流)以一个 .pipe()方法进行桥接。

Node.js中的流(Stream)介绍

伪代码的表现形式为:

//上游.pipe(下游)

Readable.pipe(Writable);

流的分类

这里并不打算讨论所谓的Node  v0.4 之前的“经典”流。那么,流分为这么几类(皆为抽象接口:

1.stream.Readable    可读流(需要实现_read方法,关注点在于对数据流读取的细节
2.stream.Writable     可写流(需要实现_write方法,关注点在于对数据流写入的细节
3.stream.Duplex        可读/写流(需要实现以上两接口,关注点为以上两接口的细节
4.stream.Transform  继承自Duplex(需要实现_transform方法,关注点在于对数据块的处理

简单来说:

1).pipe() 的拥有者一定具备 Readable 流(并不局限于)能力,它拥有 'readable'/'data'/'end'/'close'/'error' 一系列事件可供订阅,也提供 .read()/.pause()/.resume()等一系列方法供调用;
2).pipe() 的参数一定具备Writable 流(并不局限于 )能力,它拥有 'drain'/'pipe'/'unpipe'/'error'/'finish' 事件可供访问,也提供 .write()/.end() 等一系列方法供调用

什么鬼

有没有一丝丝焦虑?别急,做为一个说人话的低级码工,我会把Stream掰开了和您扯一扯的。

Stream类,在 Node.js的源码 里,是这么定义的:

var EE = require('events').EventEmitter;

var util = require('util');

util.inherits(Stream, EE);

 

function Stream() {

  EE.call(this);

}

可以看出,本质上,Stream是一个EventEmitter,那意味着它具备事件驱动的功能(.emit/.on...)。众所周知,“Node.js 就是基于V8的事件驱动平台”,实现了事件驱动的流式编程,具备了和Node一样的异步回调的特征。

比如在 Readable 流中,有一个 readable 事件,在一个暂停的只读流中,只要有数据块准备好可读时,它就会被发送给订阅者(Readable 流有哪些呢?express中的 req,ftp或者mutli-form上传组件的req.part,系统中的标准输入 process.stdin等)。有了readable 事件,我们可以做个处理shell 命令输出的分析器之类的工具:

process.stdin.on('readable', function(){

   var buf = process.stdin.read();

   if(buf){

      var data = buf.toString();

      // parsing data ...                                                

   }

});

这样调用:

head -10 some.txt | node parser.js

对于 Readable 流,我们还可以订阅它的 data 和 end 事件,以获取数据块并在流枯竭时获得通知,如 经典socket示例 中那样:

req.on('connect', function(res, socket, head) {

    socket.on('data', function(chunk) {

      console.log(chunk.toString());

    });

    socket.on('end', function() {

      proxy.close();

    });

  });

Readable流状态的切换
需要注意的是,Readable 流有两种状态:flowing mode(激流) 和 pause  mode(暂停)。前者根本停不下来,谁被pipe上了就马上不停的给;后者会暂停,直到下游显式的调用 Stream.read() 请求才读取数据块。Readable 流初始化时是 pause mode的。

这两种状态可以互为切换的,其中,

有以下任一行为,pause 转 flowing:

1.对 Readable 流添加一个data事件订阅
2.对 Readable 调用 .resume() 显式开启flowing
3.调用 Readable 流的 .pipe(writable) ,桥接到一个 Writable 流上

有以下任一行为,flowing 转回 pause:

1.Readable 流还没有 pipe 到任何流上,可调 .pause() 暂停
2.Readable 流已经 pipe 到了流上,需 remove 掉所有 data 事件订阅,并且调用 .unpipe()方法逐一解除与下游流的关系

妙用

结合流的异步特性,我可以写出这样的应用:直接将 用户A 的输出桥接到 用户B 的页面上输出:

router.post('/post', function(req, res) {

    var destination = req.headers['destination']; //发给谁

    cache[destionation] = req;

    //是的,并不返回,所以最好是个ajax请求

});

用户B请求的时候:

router.get('/inbox', function(req, res){

    var user = req.headers['user'];

    cache.find(user, function(err, previousReq){ //找到之前存的req

       var form = new multiparty.Form();

       form.parse(previousReq);  // 有文件给我

       form.on('part', function (part) {

            part.pipe(res); //流式大法好:)

 

            part.on('error', function (err) {

                console.log(err);

                messaging.setRequestDone(uniqueID);

                return res.end(err);

            });

        });

    });

});

参考

how to write node programs with streams: stream-handbook

Javascript 相关文章推荐
jquery中dom操作和事件的实例学习 下拉框应用
Dec 01 Javascript
ExtJS4中的requires使用方法示例介绍
Dec 03 Javascript
JavaScript中的Math.LOG2E属性使用详解
Jun 14 Javascript
jQuery实现的给图片点赞+1动画效果(附在线演示及demo源码下载)
Dec 31 Javascript
纯JS代码实现一键分享功能
Apr 20 Javascript
jQuery验证插件validate使用详解
May 11 Javascript
JS实现图片上传预览功能
Nov 21 Javascript
浅谈webpack对样式的处理
Jan 05 Javascript
JS中touchstart事件与click事件冲突的解决方法
Mar 12 Javascript
原生js实现公告滚动效果
Jan 10 Javascript
微信小程序拍照和摄像功能实现方法示例
Feb 01 Javascript
js函数柯里化的方法和作用实例分析
Apr 11 Javascript
Node.js 异步编程之 Callback介绍(一)
Mar 30 #Javascript
js动态修改表格行colspan列跨度的方法
Mar 30 #Javascript
jQuery使用hide方法隐藏页面上指定元素的方法
Mar 30 #Javascript
jquery使用hide方法隐藏指定id的元素
Mar 30 #Javascript
jQuery使用hide方法隐藏指定元素class样式用法实例
Mar 30 #Javascript
jQuery使用hide方法隐藏元素自身用法实例
Mar 30 #Javascript
javascript实现控制浏览器全屏
Mar 30 #Javascript
You might like
PHP中数组合并的两种方法及区别介绍
2012/09/14 PHP
round robin权重轮循算法php实现代码
2016/05/28 PHP
php连接MSsql server的五种方法总结
2018/03/04 PHP
小程序微信退款功能实现方法详解【基于thinkPHP】
2019/05/05 PHP
一端时间轮换的广告
2006/06/26 Javascript
有趣的javascript数组定义方法
2010/09/10 Javascript
JavaScript限定复选框的选择个数示例代码
2013/08/25 Javascript
zTree插件之多选下拉菜单实例代码
2013/11/06 Javascript
js中传递特殊字符(+,&)的方法
2014/01/16 Javascript
JavaScript自定义数组排序方法
2015/02/12 Javascript
Spring MVC中Ajax实现二级联动的简单实例
2016/07/06 Javascript
js实现一个可以兼容PC端和移动端的div拖动效果实例
2016/12/09 Javascript
nodejs redis 发布订阅机制封装实现方法及实例代码
2016/12/15 NodeJs
jQuery的中 is(':visible') 解析及用法(必看)
2017/02/12 Javascript
Vue ElementUI之Form表单验证遇到的问题
2017/08/21 Javascript
Vue.js用法详解
2017/11/13 Javascript
jQuery实现模糊查询的方法分析
2018/05/10 jQuery
Vue.js中该如何自己维护路由跳转记录
2019/05/19 Javascript
[14:20]刀塔大凶女神互压各路奇葩屌丝
2014/05/16 DOTA
python装饰器深入学习
2018/04/06 Python
python中实现将多个print输出合成一个数组
2018/04/19 Python
Python2实现的图片文本识别功能详解
2018/07/11 Python
linux 下python多线程递归复制文件夹及文件夹中的文件
2020/01/02 Python
python GUI库图形界面开发之PyQt5布局控件QVBoxLayout详细使用方法与实例
2020/03/06 Python
python GUI模拟实现计算器
2020/06/22 Python
Windows下PyCharm配置Anaconda环境(超详细教程)
2020/07/31 Python
Ancheer官方户外和运动商店:销售电动自行车
2019/08/07 全球购物
判断单链表中是否存在环
2012/07/16 面试题
"引用"与多态的关系
2013/02/01 面试题
进程的查看和调度分别使用什么命令
2015/03/25 面试题
专科毕业生求职简历的自我评价
2013/10/12 职场文书
大专毕业生自我评价分享
2013/11/10 职场文书
行政部工作岗位职责范本
2014/03/05 职场文书
行政管理专业求职信
2014/07/06 职场文书
爱心捐助活动总结
2015/05/09 职场文书
Nginx如何获取自定义请求header头和URL参数详解
2022/07/23 Servers