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 学习笔记 选择器之六
Jul 23 Javascript
基于JavaScript实现继承机制之构造函数方法对象冒充的使用详解
May 07 Javascript
js获取当前路径的简单示例代码
Jan 08 Javascript
javascript实现的一个随机点名功能
Aug 26 Javascript
JavaScript常用脚本汇总(二)
Mar 04 Javascript
Canvas 制作动态进度加载水球详解及实例代码
Dec 09 Javascript
详解vue2 $watch要注意的问题
Sep 08 Javascript
微信小程序出现wx.navigateTo页面不跳转问题的解决方法
Dec 26 Javascript
Vue-router 切换组件页面时进入进出动画方法
Sep 01 Javascript
Vue 自定义指令实现一键 Copy功能
Sep 16 Javascript
vue-router结合vuex实现用户权限控制功能
Nov 14 Javascript
解决vue里a标签值解析变量,跳转页面,前面加默认域名端口的问题
Jul 22 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
2006/10/09 PHP
php实现的仿阿里巴巴实现同类产品翻页
2009/12/11 PHP
PHP 转义使用详解
2013/07/15 PHP
一个不易被发现的PHP后门代码解析
2014/07/05 PHP
php中上传文件的的解决方案
2018/09/25 PHP
PHP JWT初识及其简单示例
2018/10/10 PHP
浅析Node在构建超媒体API中的作用
2014/07/30 Javascript
js使用html()或text()方法获取设置p标签的显示的值
2014/08/01 Javascript
node.js中的fs.fstat方法使用说明
2014/12/15 Javascript
浅谈javascript 函数属性和方法
2015/01/21 Javascript
JavaScript采用递归算法计算阶乘实例
2015/08/04 Javascript
浅析Bootstrap缩略图组件与警示框组件
2016/04/29 Javascript
基于iScroll实现下拉刷新和上滑加载效果
2017/07/18 Javascript
jquery的 filter()方法使用教程
2018/03/22 jQuery
vue实现数字动态翻牌的效果(开箱即用)
2019/12/08 Javascript
js判断一个对象是数组(函数)的方法实例
2019/12/19 Javascript
[02:43]DOTA2英雄基础教程 德鲁伊
2014/01/13 DOTA
Python Sql数据库增删改查操作简单封装
2016/04/18 Python
对命令行模式与python交互模式介绍
2018/05/12 Python
python保存网页图片到本地的方法
2018/07/24 Python
解决Python3.5+OpenCV3.2读取图像的问题
2018/12/05 Python
DataFrame:通过SparkSql将scala类转为DataFrame的方法
2019/01/29 Python
Django Rest framework认证组件详细用法
2019/07/25 Python
pandas将list数据拆分成行或列的实现
2020/12/13 Python
Python jieba库分词模式实例用法
2021/01/13 Python
HTML5 UTF-8 中文乱码的解决方法
2013/11/18 HTML / CSS
日语系毕业生推荐信
2013/11/11 职场文书
受欢迎的大学生自我评价
2013/12/05 职场文书
水利水电建筑施工应届生求职信
2014/07/04 职场文书
2014年工程师工作总结
2014/11/25 职场文书
基层组织建设年活动总结
2015/05/09 职场文书
2016年六一文艺汇演开幕词
2016/03/04 职场文书
CSS3新特性详解(五):多列columns column-count和flex布局
2021/04/30 HTML / CSS
Python中for后接else的语法使用
2021/05/18 Python
SQL优化老出错,那是你没弄明白MySQL解释计划用法
2021/11/27 MySQL
《巫师》是美食游戏?CDPR10月将推出《巫师》官方食谱
2022/04/03 其他游戏