读取流

通过这种方式我们可以创建一个Readable实例。

// 这样创建不会抛出任何异常
var reader = new Readable();
// 打印一下这个对象有什么吧
console.log(reader);

打印结果:

{ _readableState:
   { highWaterMark: 16384,
     buffer: [],
     length: 0,
     pipes: null,
     pipesCount: 0,
     flowing: false,
     ended: false,
     endEmitted: false,
     reading: false,
     calledRead: false,
     sync: true,
     needReadable: false,
     emittedReadable: false,
     objectMode: false,
     ranOut: false,
     awaitDrain: 0,
     readingMore: false,
     decoder: null },
readable: true,
  domain: null,
  _events: {},
  _maxListeners: 10 }

哇!东西可真多!

看到_readableState对象内部的属性了吗?这是表示readable内部状态的,当然这是私有的,根据“某某不平等条约”不应该更改这些属性。不过可以通过合法的方式设置highWaterMark、encoding和objectMode三个属性。

通过给构造函数传值

var Readable = require("stream").Readable;

var reader = new Readable({
            objectMode:true,
            encoding:"utf16le",
            highWaterMark:1024*10});

console.log(reader);

打印部分结果:

{ _readableState:
   { highWaterMark: 10240,
     objectMode: true,
     decoder:
      { encoding: 'utf16le' }
}

会发现确实改变了这三个属性,其余属性都无法更改,属于内部属性了。

encoding属性默认为null。用于Buffer编码所用。

highWaterMark属性默认是16KB,相当于一个量杯,从底层读取数据保存在内部一个“小桶”里,当小桶装满后或底层资源就这么多时,就会返回(读取)一桶数据,如果底层还有数据,那么就继续装桶,这个桶的最大值就是highWaterMark定义的。当然这个依赖实现类,实现类实现_read方法。

objectMode属性默认为false,表示调用read(size)时,根据size大小返回底层数据;objectMode=true 时,size参数失效,会返回内部储存的数据。(这个不太好理解,后面会用例子讲解清楚)。

OK,实现一个Readable类,代码如下:

var Readable = require("stream").Readable;

function TestRead(data,options){
    this._buf = data;
    this._po = 0;
    Readable.call(this,options);
}

TestRead.prototype = Object.create(Readable.prototype,{
    constructor:{value:TestRead},
    _read:{value:function(n){
       if(this._po >= this._buf.length){
        this.push(null);
       }else{
        var ct = this._buf.slice(this._po,this._po+n>this._buf.length?this._buf.length:this._po+n);
        this.push(ct);
       }
       this._po += n;
    }
    }
})

测试这个Readable子类TestReadsex

// 要读取的数据  
var buf  = new Buffer([
    0x12,0x22,
    0x32,0x11,
    0x22,0x33,
    0x23,0x23,
    0x44,0x22,
    0x34,0x56,
    0x91,0x90,
    0x43]);

// 实例化个对象
var read = new TestRead(buf,{highWaterMark:2});

console.log(read.read(2))
console.log(read.read(1))
console.log(read.read(3))

运行结果

<Buffer 12 22>
<Buffer 32>
<Buffer 11 22 33>

通过这个结果,可以看出,默认objectMode = false时,那么read(size)可以访问byte数量。如果size越除了底层数据,那么返回null。

当 objectMode = true,看例子结果。

// 实例化个对象
var read = new TestRead(buf,{highWaterMark:2,objectMode:true});

console.log(read.read(2))
console.log(read.read(1))
console.log(read.read(3))

打印结果是:

<Buffer 12 22>
<Buffer 32 11>
<Buffer 22 33>

这说明,每次read返回的是内部储存的内容,而size失效。

readable._read(size)

这应该是实现子类调用的方法,不应该由外界调用。所有Readable子类必须通过实现这个方法,达到从底层读取数据的功能。

size参数是个参考参数,这个参数就是 highWaterMark定义的;这个参数的含义,是每次从底层数据读取的最大byte数量。每次调用都应该底层读取一段新的数据,读取到数据后,通过this.push(data)方法推送到读队列。(参看前面的例子)

readable.push(data)

data可以是Buffer|String|NULL,方法返回Boolean值,当push(false)时,就会retrun false,否则true。

这个方法应该是由_read方法内部调用的,当第一次push数据到读队列,内部会触发“readable”事件,当push(null) 时,内部会触发end事件。

readable.unshift(data)

顾名思义,就是push方法的反方向,也就是插入到数据块前方。其余功能和特性与push一样。

readable.setEncoding(encoding)

这个是设置编码的,底层数据返回的是String类型的数据时有效,也可以在构造函数设置,前面已提到。

readable.read([size])

前面根据实例介绍了read方法,read方法和_read方法不是一个概念,要分开理解。

fuck调用 read之后才会激发 "readable" 事件。有几种情况:

当objectMode = true时, read(0)将返回null,没什么现象和作用,read()会返回当前缓冲区数据,并刷新缓冲区。read(2)或read(18)效果和read()效果一致。

当objectMode = false时,read(0)将返回null,并且缓冲区不会刷新,会把下一次的push数据和缓冲区的数据合并。read()会返回当前缓冲区数据,并刷新缓冲区,这个和objectMode=true时的情况一致。read(n) 情况比较复杂,第一种情况:n大小在原始数据范围内时,会返回相应多的数据,并且会把highWaterMark变成n大小,如果再调用read(n2)一次,那么highWaterMark会再次增大。第二种情况,n大小超出原始数据范围时,会返回null,hightWaterMark和第一种情况一样增大,但不会消耗数据,这种情况下data事件会直接获得全部数据。

readable.pause();

暂停流读取。并执行一次 readable.read(),注意是不参数read()。

readable.resume();

恢复流读取。并执行一次 readable.read()。