精华 可读 stream 的 readable 事件什么时候触发?
发布于 3个月前 作者 newcallback 438 次浏览 来自 问答

场景

在看 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事件可能会带来程序内存泄露
  • 尽量使用被动模式,避免使用经典模式

感谢楼上大神愿意花时间回复!学习了! 另外想咨询下,这些知识点你是怎么知道的啊?从哪学到的呢?

非常感谢 :-)

@newcallback 我先把你的代码重写了一遍,理解到你的想法。然后就开始谷歌了,结合官方的stream文档,自己写一些例子测测。一般就这样。

回到顶部