场景
在看 stream 时发现除了很多很常见的 data 事件外,还有一个 readable 事件。 而且 node 官方 api 上说 “Once the internal buffer is drained, a readable event will fire again when more data is available.”
问题
readable 事件似乎是可以被多次触发的,那么它的触发机制是怎样的?跟 data 事件的先后关系是怎样的呢?
尝试求解过程:
为了找到答案,我写了下面的测试代码,可结果更让我诧异。 我的测试代码(如下),运行结果(代码下方)为什么是这样?
var Readable = stream.Readable
function testRead(options) {
Readable.call(this, options)
this.n = 0
}
util.inherits(testRead, Readable)
testRead.prototype._read = function(len) {
if (this.n > 4) {
this.n++
console.log('push_null')
this.push(null)
} else {
console.log('push_' + this.n)
this.push((this.n++) + '')
}
}
var f = new testRead()
f.on('readable', function() {
console.log('readable')
})
f.on('data', function(data) {
console.log(data.toString())
})
f.on('end', function() {
console.log('end')
})
console.log 结果: push_0 readable push_1 01 push_2 2 push_3 3 push_4 4 push_null readable readable readable readable readable end
求助各位大神!先感谢各位了!
4 回复
readable事件和data事件的触发时机是一样的,都是read流数据从无到有,就触发。所以在读取一个较大的文件时,他们都会多次触发。
但是readable和data事件的设计目的是不一样的,他们不能同时使用。
Read流有两种读取数据的模式
-
经典模式: read流主动读取数据,发送data事件。不管接收端是否完成处理,重复上述步骤
-
被动模式: read流不主动读取数据,当接收端向他索要数据时,才读取。
可以看到,data事件适用于经典模式,readable事件适用于被动模式。
使用readable事件的例子:
readStream.on('readable',function(){
var chunk;
while(null !== (chunk = readStream.read())){
dealWith(chunk)
}
})
.on('end',function(){
//end
})
你要留意的是,如果你为read流注册了data事件监听,那么read流就隐性的使用经典模式读取数据。
比较这两个例子:
/**
*console输出
*01234
*/
var Readable = require('stream').Readable;
var rs = new Readable;
var n = 0;
rs._read = function(){
if(n>4){
n++;
rs.push(null);
} else {
rs.push((n++) + '');
}
}
var str='';
rs.on('readable',function(){
var chunk;
while(null !== (chunk=rs.read())){
str += chunk.toString('utf8')
}
})
rs.on('data',function(data){
console.log('data ' + data);
})
rs.on('end',function(){
console.log(str)
})
/**
*console输出
*data 0
*data 1
*data 2
*data 3
*data 4
*/
var Readable = require('stream').Readable;
var rs = new Readable;
var n = 0;
rs._read = function(){
if(n>4){
n++;
rs.push(null);
} else {
rs.push((n++) + '');
}
}
rs.on('data',function(data){
console.log('data ' + data);
})
var str='';
rs.on('readable',function(){
var chunk;
while(null !== (chunk=rs.read())){
str += chunk.toString('utf8')
}
})
rs.on('end',function(){
console.log(str)
})
总结
- data事件和readable事件不要同时使用
- data事件可能会带来程序内存泄露
- 尽量使用被动模式,避免使用经典模式