该文章整合了多篇网络文章(整合之处已设置超连接,可点击直接了解原文),目的仅仅是为了和大伙分享,更加通俗易懂的了解流的各个流程的初始。本人也是node的初学菜鸟,有描述错误或误人子弟的地方多请大神们多多指出。html
读缓冲区(readable buffer):这里的读是个形容词,是指可读流临时存放data(只能是字符串或者Buffer,不能是数字)的缓冲区。(读缓冲区就像一个水电站同样,感受这样描述比较好理解flowing、paused模式)node
flowing模式:即流动模式,就像打开水电站的水闸同样,上游的水和下游完彻底全连通直到上游来源的数据耗尽。json
paused模式:即暂停模式,就像水电站的水闸在你指定的时候(使用stream.read())才会打开。不过,当你使用read()打开水闸的时候是一个超天然现象---水电站里的水瞬间被抽干,上游的水还没来得及填充水电站。而后自动关闭水闸,等待你的下一次“惠顾“read()。网络
_read:上游水源经过_read里中的push、unshift方法流入水电站中。函数
readable:在数据块能够从流中读取的时候发出。它对应的处理器没有参数,能够在处理器里调用read([size])方法读取数据。ui
data:有数据可读时发出。它对应的处理器有一个参数,表明数据。若是你只想快快地读取一个流的数据,给data关联一个处理器是最方便的办法。处理器的参数是Buffer对象,若是你调用了Readable的setEncoding(encoding)方法,处理器的参数就是String对象。this
end:当数据被读完时发出。对应的处理器没有参数。编码
close:当底层的资源,如文件,已关闭时发出。不是全部的Readable流都会发出这个事件。对应的处理器没有参数。spa
error:当在接收数据中出现错误时发出。对应的处理器参数是Error的实例,它的message属性描述了错误缘由,stack属性保存了发生错误时的堆栈信息。.net
read([size]):若是你给read方法传递了一个大小做为参数,那它会返回指定数量的数据,若是数据不足,就会返回null。若是你不给read方法传参,它会返回内部缓冲区里的全部数据,若是没有数据,会返回null,此时有可能说明遇到了文件末尾。read返回的数据多是Buffer对象,也多是String对象。
setEncoding(encoding):给流设置一个编码格式,用于解码读到的数据。调用此方法后,read([size])方法返回String对象。
pause():暂停可读流,再也不发出data事件
resume():恢复可读流,继续发出data事件
pipe(destination,[options]):把这个可读流的输出传递给destination指定的Writable流,两个流组成一个管道。options是一个JS对象,这个对象有一个布尔类型的end属性,默认值为true,当end为true时,Readable结束时自动结束Writable。注意,咱们能够把一个Readable与若干Writable连在一块儿,组成多个管道,每个Writable都能获得一样的数据。这个方法返回destination,若是destination自己又是Readable流,就能够级联调用pipe(好比咱们在使用gzip压缩、解压缩时就会这样,立刻会讲到)。
unpipe([destination]):端口与指定destination的管道。不传递destination时,断开与这个可读流连在一块儿的全部管道。
经过添加 data 事件监听器来启动数据监听
调用 resume() 方法启动数据流
调用 pipe() 方法将数据转接到另外一个 可写流
在流没有 pipe() 时,调用 pause() 方法能够将流暂停
pipe() 时,须要移除全部 data 事件的监听,再调用 unpipe() 方法
data listener
readable listener
read()——若是当前缓冲区为空,或者缓冲区并未超出咱们设定的最大值,那么就能够继续准备数据;若是此时正在准备数据(_read())或者已经结束读取(push(null)),那么就放弃准备数据。
1.在paused模式下则读取所有缓冲区的长度;若读取的字节数(n)大于设置的缓冲区最大值,则适当扩大缓冲区的大小(默认为16k,最大为8m);若读取的长度大于当前缓冲区的大小,设置needReadable属性并准备数据等待下一次读取。
2.若是当前缓冲区为空,或者缓冲区并未超出咱们设定的最大值,那么就能够继续准备数据;若是此时正在准备数据(_read())或者已经结束读取(push(null)),那么就放弃准备数据。
3.针对这个私有方法_read,文档上有特殊说明,自定义的Readable实现类须要实现这个方法,在该方法中手动添加数据到Readable对象的读缓冲区,而后进行Readable的读取。能够理解为_read函数为读取数据前的准备工做(准备数据),针对的是流的实现者而言。
1.对于处在flowing模式下的读取,每次只读缓冲区中第一个buffer的长度
2.针对这个私有方法_read,文档上有特殊说明,自定义的Readable实现类须要实现这个方法,在该方法中手动添加数据到Readable对象的读缓冲区,而后进行Readable的读取。能够理解为_read函数为读取数据前的准备工做(准备数据),针对的是流的实现者而言。
//这是一个将存放多条json字符串的txt文件读取成json的例子 const stream = require('stream'); const fs = require('fs'); const util = require('util'); function JSONLineReader(source) { stream.Readable.call(this); this._source = source; this._foundLineEnd = false; this._buffer = ''; source.on('readable', function() {//监听source何时准备好,那么咱们就能够用read()或则readable listener去触发JSONLineReader的_read方法 this.read(); // this.on('readable', function(data) { // console.log('readable'); // }); }.bind(this)) } util.inherits(JSONLineReader, stream.Readable); JSONLineReader.prototype._read = function(size) { var chunk; var line; var lineIndex; var result; if (this._buffer.length === 0) { chunk = this._source.read(); this._buffer += chunk; //一次就拿完 只是看何时push null } lineIndex = this._buffer.indexOf('\n'); if (lineIndex !== -1) { line = this._buffer.slice(0, lineIndex); if (line) { result = JSON.parse(line); this._buffer = this._buffer.slice(lineIndex + 1); this.emit('object', result);util.inspect(result)) this.push(util.inspect(result)); } else { this._buffer = this._buffer.slice(1); } } } let input = fs.createReadStream(__dirname + '/json-lines.txt', { encoding: 'utf8' }); var jsonLineReader = new JSONLineReader(input); jsonLineReader.on('object', function(obj) { console.log('pos:', obj); }) /*json-lines.txt {"success":false,"code":501} {"success":true,"code":202} {"success":false,"code":503} {"success":true,"code":204} {"success":false,"code":505} {"success":true,"code":206} {"success":false,"code":507} {"success":true,"code":208} {"success":false,"code":509} */
let stream = require('stream'); let util = require('util'); util.inherits(flowingReadableDemo, stream.Readable); function flowingReadableDemo(opt) { stream.Readable.call(this, opt); this.quotes = ["yessdasdsa", "noasdasdas", "maybe"]; this._index = 0; } flowingReadableDemo.prototype._read = function() { if (this._index >= this.quotes.length) { this.push(null); } else { this.push(this.quotes[this._index]); this._index += 1; } }; let r = new flowingReadableDemo(); r.on('data', function(data) { console.log("Callback read: " + data.toString()); // flowing状态下,咱们无需执行read,仅须要设置data事件处理函数或者设定导流目标pipe }); r.on('end', function(data) { console.log("No more answers."); });