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数据缓存系统实现代码
Oct 24 Javascript
JavaScript 匿名函数和闭包介绍
Apr 13 Javascript
js+html5实现canvas绘制镂空字体文本的方法
Jun 05 Javascript
JQuery实现鼠标滚轮滑动到页面节点
Jul 28 Javascript
JavaScript实现移动端滑动选择日期功能
Jun 21 Javascript
归纳下js面向对象的几种常见写法总结
Aug 24 Javascript
angular.js 路由及页面传参示例
Feb 24 Javascript
JS实现的简单表单验证功能示例
Oct 13 Javascript
原生JavaScript实现Ajax异步请求
Nov 19 Javascript
简单谈谈CommonsChunkPlugin抽取公共模块
Dec 31 Javascript
详解ES6通过WeakMap解决内存泄漏问题
Mar 09 Javascript
微信小程序对图片进行canvas压缩的方法示例详解
Nov 12 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 smarty模版引擎中的缓存应用
2009/12/11 PHP
Docker搭建自己的PHP开发环境
2018/02/24 PHP
Yii框架getter与setter方法功能与用法分析
2019/10/22 PHP
Git命令之分支详解
2021/03/02 PHP
建议大家看下JavaScript重要知识更新
2007/07/08 Javascript
js替换字符串的所有示例代码
2013/07/23 Javascript
jquery获取当前点击对象的value方法
2014/02/28 Javascript
jQuery中element选择器用法实例
2014/12/29 Javascript
jQuery及JS实现循环中暂停的方法
2015/02/02 Javascript
JQuery的ON()方法支持的所有事件罗列
2015/02/28 Javascript
jquery图形密码实现方法
2015/03/11 Javascript
javascript删除数组重复元素的方法汇总
2015/06/24 Javascript
JavaScript采用递归算法计算阶乘实例
2015/08/04 Javascript
以JavaScript来实现WordPress中的二级导航菜单的方法
2015/12/14 Javascript
jquery+css3实现熊猫tv导航代码分享
2018/02/12 jQuery
Vue CL3 配置路径别名详解
2019/05/30 Javascript
Vue实现页面添加水印功能
2019/11/09 Javascript
vue调用微信JSDK 扫一扫,相册等需要注意的事项
2021/01/03 Vue.js
[01:00:13]完美世界DOTA2联赛 LBZS vs Forest 第一场 11.07
2020/11/09 DOTA
让代码变得更易维护的7个Python库
2018/10/09 Python
python的set处理二维数组转一维数组的方法示例
2019/05/31 Python
css3实现input输入框颜色渐变发光效果代码
2014/04/02 HTML / CSS
HTML5之多线程(Web Worker)
2019/01/02 HTML / CSS
HTML5不支持frameset的两种解决方法
2016/11/14 HTML / CSS
艺术专业大学生自我评价
2013/09/22 职场文书
优秀毕业生自荐信范文
2014/01/01 职场文书
航海技术专业毕业生求职信
2014/04/06 职场文书
网站推广策划方案
2014/06/04 职场文书
个人查摆剖析材料
2014/10/04 职场文书
关于运动会的广播稿50字
2014/10/17 职场文书
房地产销售主管岗位职责
2015/02/13 职场文书
入党转正申请报告
2015/05/15 职场文书
2016年五四青年节校园广播稿
2015/12/17 职场文书
python基于turtle绘制几何图形
2021/06/15 Python
golang内置函数len的小技巧
2021/07/25 Golang
MySql按时,天,周,月进行数据统计
2022/08/14 MySQL