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 相关文章推荐
javascript实现的动态文字变换
Jul 28 Javascript
基于jquery的网页SELECT下拉框美化代码
Oct 28 Javascript
javascript中负数算术右移、逻辑右移的奥秘探索
Oct 17 Javascript
JQuery仿小米手机抢购页面倒计时效果
Dec 16 Javascript
jQuery mobile转换url地址及获取url中目录部分的方法
Dec 04 Javascript
JQUERY的AJAX请求缓存里的数据问题处理
Feb 23 Javascript
jQuery遍历节点树方法分析
Sep 08 Javascript
JavaScript实现垂直向上无缝滚动特效代码
Nov 23 Javascript
基于JavaScript实现本地图片预览
Feb 08 Javascript
js css自定义分页效果
Feb 24 Javascript
JS实现简单tab选项卡切换
Oct 25 Javascript
JavaScript字符串转数字的简单实现方法
Nov 27 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 Notice: Undefined index 错误提示解决方法
2010/08/29 PHP
php笔记之:php函数range() round()和list()的使用说明
2013/04/26 PHP
调试PHP程序的多种方法介绍
2014/11/06 PHP
thinkPHP基于ajax实现的菜单与分页示例
2016/07/12 PHP
老生常谈文本文件和二进制文件的区别
2017/02/27 PHP
laravel 判断查询数据库返回值的例子
2019/10/11 PHP
ie和firefox中img对象区别的困惑
2006/12/27 Javascript
Js 获取当前日期时间及其它操作实现代码
2021/03/04 Javascript
js实现广告漂浮效果的小例子
2013/07/02 Javascript
struts2+jquery组合验证注册用户是否存在
2014/04/30 Javascript
JS模拟并美化的表单控件完整实例
2015/08/19 Javascript
详解AngularJS过滤器的使用
2016/03/11 Javascript
javascript操作cookie
2017/01/17 Javascript
解决Node.js使用MySQL出现connect ECONNREFUSED 127.0.0.1:3306的问题
2017/03/09 Javascript
nodejs模块nodemailer基本使用-邮件发送示例(支持附件)
2017/03/28 NodeJs
详解Angular操作cookies方法
2018/06/01 Javascript
vue.js表单验证插件(vee-validate)的使用教程详解
2019/05/23 Javascript
Python正则简单实例分析
2017/03/21 Python
Python实现文件内容批量追加的方法示例
2017/08/29 Python
详解python之协程gevent模块
2018/06/14 Python
Python多进程写入同一文件的方法
2019/01/14 Python
详解python列表生成式和列表生成式器区别
2019/03/27 Python
python七夕浪漫表白源码
2019/04/05 Python
Python flask框架post接口调用示例
2019/07/03 Python
python实现同一局域网下传输图片
2020/03/20 Python
CSS3制作苹果风格键盘特效
2015/02/26 HTML / CSS
环境科学专业个人求职信
2013/09/26 职场文书
护理专业个人求职简历的自我评价
2013/10/13 职场文书
留学自荐信的技巧
2013/10/17 职场文书
5s推行计划书
2014/05/06 职场文书
乒乓球兴趣小组活动总结
2014/07/08 职场文书
交通事故赔偿协议书怎么写
2014/10/04 职场文书
学校实习推荐信
2015/03/27 职场文书
2015年医药代表工作总结
2015/04/25 职场文书
SQL Server2019数据库之简单子查询的具有方法
2021/04/27 SQL Server
Vue ECharts实现机舱座位选择展示功能
2022/05/15 Vue.js