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 相关文章推荐
ajax不执行success回调而是执行了error回调
Dec 10 Javascript
基于iframe实现类似于ajax的页面无刷新
May 31 Javascript
Shell脚本实现Linux系统和进程资源监控
Mar 05 Javascript
使用jspdf生成pdf报表
Jul 03 Javascript
JS基于面向对象实现的拖拽库实例
Sep 24 Javascript
JS返回页面时自动回滚到历史浏览位置
Sep 26 Javascript
简述vue-cli中chainWebpack的使用方法
Jul 30 Javascript
vue进入页面时不在顶部,检测滚动返回顶部按钮问题及解决方法
Oct 30 Javascript
解决Vue大括号字符换行踩的坑
Nov 09 Javascript
vue+elementui通用弹窗的实现(新增+编辑)
Jan 07 Vue.js
jQuery是用来干什么的 jquery其实就是一个js框架
Feb 04 jQuery
比较node.js和Deno
Apr 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 代码优化之经典示例
2011/03/24 PHP
php中static和const关键字用法分析
2016/12/07 PHP
php使用PDO执行SQL语句的方法分析
2017/02/16 PHP
PHP命名空间namespace及use的简单用法分析
2018/08/03 PHP
javascript instanceof 内部机制探析
2010/10/15 Javascript
Javascript中typeof 用法小结
2015/05/12 Javascript
JS实现将数字金额转换为大写人民币汉字的方法
2016/08/02 Javascript
jQuery EasyUI Accordion可伸缩面板组件使用详解
2017/02/28 Javascript
node.js 抓取代理ip实例代码
2017/04/30 Javascript
JavaScript实现一个空中避难的小游戏
2017/06/06 Javascript
解决element UI 自定义传参的问题
2018/08/22 Javascript
详解微信小程序中组件通讯
2018/10/30 Javascript
Vue+Vant 图片上传加显示的案例
2020/11/03 Javascript
[00:56]2014DOTA2国际邀请赛 DK、iG 赛前探访
2014/07/10 DOTA
[33:23]VG vs Pain 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
[42:20]Winstrike vs VGJ.S 2018国际邀请赛淘汰赛BO3 第二场 8.23
2018/08/24 DOTA
[53:52]OG vs EG 2018国际邀请赛淘汰赛BO3 第二场 8.23
2018/08/24 DOTA
Python实现的购物车功能示例
2018/02/11 Python
django解决跨域请求的问题详解
2019/01/20 Python
Python使用OpenPyXL处理Excel表格
2020/07/02 Python
在 Python 中使用 7zip 备份文件的操作
2020/12/11 Python
matplotlib自定义鼠标光标坐标格式的实现
2021/01/08 Python
CSS中的字体大小设置属性总结
2016/05/24 HTML / CSS
iRobot官网:改变生活的家用机器人品牌
2016/09/20 全球购物
美国受欢迎的女性牛仔裤品牌:DL1961
2016/11/12 全球购物
澳大利亚最好的厨具店:Kitchen Warehouse
2018/03/13 全球购物
C语言怎样定义和声明全局变量和函数最好
2013/11/26 面试题
《手指教学》反思
2014/02/14 职场文书
三查三看党性分析材料
2014/02/18 职场文书
积极贯彻学习两会精神总结
2014/03/17 职场文书
单方离婚协议书范本(2014版)
2014/09/30 职场文书
2014年办公室主任工作总结
2014/11/12 职场文书
青年人初次创业的“五不要”
2019/08/23 职场文书
python实现简易自习室座位预约系统
2021/06/30 Python
Java存储没有重复元素的数组
2022/04/29 Java/Android
Spring boot实现上传文件到本地服务器
2022/08/14 Java/Android