zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Node.js stream模块(四)双工流和转换流

2023-09-14 09:13:41 时间

stream模块除了可读流Readable,可写流Writable,还有双工流Duplex和转换流Transform。

Duplex

所谓双工流,就是既能作为可读流,也能作为可写流。

我们想要实现一个双工流,就相当于实现了一个可读流和可写流。

通常,我们需要自定义类去继承Duplex,并重写_read和_write方法。

const { Duplex } = require('stream')

class MyDuplex extends Duplex {
  constructor(source) {
    super()
    this.source = source
  }

  _read() {
    if (this.source.length > 0) {
      this.push(this.source.shift())
    } else {
      this.push(null)
    }
  }

  _write(chunk, encoding, callback) {
    console.log(chunk.toString())
    callback()
  }
}

const md = new MyDuplex(['a', 'b', 'c'])

md.on('data', (chunk) => {
  console.log(chunk.toString());
})

let flag = md.write('123')
console.log(flag);

flag = md.write('456')
console.log(flag);

let ws = md.end('789')
console.log('-->', ws === md);

通过查看双工流对象构造可知:

 双工流对象上分别维护了一个_readableState和一个_writableState,即维护了两个缓冲区,这两个缓冲区之间没有联系,即双工流对象自身生成的数据,无法被自身消费。

Transform

所谓转换流,其实本质也是一个双工流,即转换流也是既可以作为可读流,也可以作为可写流。但是转换流和双工流的区别在于:转换流的两个缓冲区之间存在联系,_readableState.buffer的数据会被写入到_writableState.buffered中,即实现自己生成,自己消费

实现转换流,一般需要自定义类去继承Transform,并重写_transform方法

而_transform方法相当于_write和_read的结合体,即_transform可以接收参数chunk,encoding,callback,并消费,也可以实现将参数chunk缓存到_readableState.buffer中

 但是这样看来,转换流并没有什么实际用处,只是将从wirte传入的数据,即本来应该被消费的数据缓存起来了而已。

确实,如果转换流单独使用的话,的确没有什么用处。

但是从转换流的名字,我们就可以知道,这个流一定是用来做数据转换操作的。

在加入了pipe方法后,流数据传输变得更加简单,但是我们无法缺失去了操作数据的机会,因为pipe方法没有提供操作数据的渠道,它只负责实现平滑的传输数据。

所以转换流就有了用武之地,比如我们想将一个可读流中的单词变成大写,再传入一个可写流输出

我们知道 pipe方法的调用者必须是可读流,pipe方法入参必须是可写流,而pipe方法的返回值就是pipe方法入参

而转换流是一个双工流,所以既可以作为pipe方法入参的可写流,也可以作为pipe调用者的可读流。

所以rs.pipe(mt),即将可读流rs中数据传入了mt转换流中,而数据进入mt后,mt的_transform方法会对数据进行转换操作,比如小写变大写。

而rs.pipe(mt)的返回值还是mt本身,而mt又可以作为可读流,所以

rs.pipe(mt).pipe是合法的

process.stdout是一个控制台输出流,即也是一个可写流,所以可以作为pipe的参数。

以上mt既作为了流数据传输的一环,也作为了流数据操作的入口。