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下数值型比较难点说明
Jun 07 Javascript
node.js中的buffer.toString方法使用说明
Dec 14 Javascript
jQuery中innerHeight()方法用法实例
Jan 19 Javascript
js实现网页右上角滑出会自动消失大幅广告的方法
Feb 27 Javascript
用jmSlip编写移动端顶部日历选择控件
Oct 24 Javascript
使用base64对图片的二进制进行编码并用ajax进行显示
Jan 03 Javascript
jquery表单验证插件validation使用方法详解
Jan 20 Javascript
ES6 Promise对象概念与用法分析
Apr 01 Javascript
Vue实现web分页组件详解
Nov 28 Javascript
超好用的jQuery分页插件jpaginate用法示例【附源码下载】
Dec 06 jQuery
Vue分页效果与购物车功能
Dec 13 Javascript
解决vue使用vant轮播组件swipe + flex时文字抖动问题
Jan 07 Vue.js
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使用array_fill定义多维数组的方法
2015/03/18 PHP
PHP动态柱状图实现方法
2015/03/30 PHP
PHP微信开发之查询城市天气
2016/06/23 PHP
解决Extjs 4 Panel作为Window组件的子组件时出现双重边框问题
2013/01/11 Javascript
JavaScript实现点击按钮后变灰避免多次重复提交
2013/07/15 Javascript
JavaScript italics方法入门实例(把字符串显示为斜体)
2014/10/17 Javascript
基于jquery和svg实现超炫酷的动画特效
2014/12/09 Javascript
JQuery显示隐藏页面元素的方法总结
2015/04/16 Javascript
jQuery 3.0 的变化及使用方法
2016/02/01 Javascript
Javascript删除指定元素节点的方法
2016/06/21 Javascript
利用Angularjs和原生JS分别实现动态效果的输入框
2016/09/01 Javascript
ES6解构赋值的功能与用途实例分析
2017/10/31 Javascript
vue init webpack myproject构建项目 ip不能访问的解决方法
2018/03/20 Javascript
vue2.0实现移动端的输入框实时检索更新列表功能
2018/05/08 Javascript
在小程序/mpvue中使用flyio发起网络请求的方法
2018/09/13 Javascript
js实现简单分页导航栏效果
2019/06/28 Javascript
关于微信小程序map组件z-index的层级问题分析
2019/07/09 Javascript
微信小程序 行的删除和增加操作实现详解
2019/09/29 Javascript
微信小程序(订阅消息)功能
2019/10/25 Javascript
Vue在chrome44偶现点击子元素事件无法冒泡的解决方法
2019/12/15 Javascript
[06:50]DSPL次级职业联赛十强晋级之路
2014/11/18 DOTA
Django实现微信小程序的登录验证功能并维护登录态
2019/07/04 Python
Python字典推导式将cookie字符串转化为字典解析
2019/08/10 Python
基于Tensorflow:CPU性能分析
2020/02/10 Python
pytorch masked_fill报错的解决
2020/02/18 Python
浅谈Tensorflow加载Vgg预训练模型的几个注意事项
2020/05/26 Python
Pytorch 使用CNN图像分类的实现
2020/06/16 Python
Python Process创建进程的2种方法详解
2021/01/25 Python
CSS3 优势以及网页设计师如何使用CSS3技术
2009/07/29 HTML / CSS
世界上最具创新性的增强型知名运动品牌:Proviz
2018/04/03 全球购物
Lentiamo丹麦:购买便宜的隐形眼镜
2021/01/13 全球购物
青年文明号服务承诺
2014/03/31 职场文书
2014-2015学年工作总结
2014/11/27 职场文书
积极心理学课程心得体会
2016/01/22 职场文书
《我是什么》教学反思
2016/02/16 职场文书
2019通用版劳动合同范本!
2019/07/11 职场文书