双工流与转换流介绍

双工流

双工流 Duplex 可以实现流的可读和可写功能,即同时实现 ReadableWritable。实现双工流需要进行以下 3 步。

  1. 继承 Duplex 类。

  2. 实现 _read() 方法。

  3. 实现 _write() 方法。

使用代码演示上面的 3 个步骤,其形式如下:

const Duplex = require('stream').Duplex;
const myDuplex = new Duplex({
     _read(size) {
          // ...
     },
     _write(chunk, encoding, callback) {
          // ...
     }
});

【例10.4】双工流的使用。(实例位置:资源包\源码\10\04)

创建一个 .js 文件,其中创建双工流对象,并分别实现双工流的 _read() 方法和 _write() 方法;然后通过监听 data 事件实现数据的读取,通过调用 write() 方法实现数据的写入;最后分别监听 end 事件和 finish 事件,确认读取和写入操作完成。代码如下:

const stream = require('stream');
var duplexStream = stream.Duplex();
duplexStream._read = function () {
     this.push('读取数据');
     this.push(null)
}
duplexStream._write = function (data, enc, next) {
     console.log(data.toString());
     next();
}
duplexStream.on('data', data => console.log(data.toString()));
duplexStream.on('end', data => console.log('读取完成'));
duplexStream.write('写入数据');
duplexStream.end();
duplexStream.on('finish', data => console.log('写入完成'));

运行结果如图10.6所示。

image 2024 04 14 00 32 14 090
Figure 1. 图10.6 使用双工流实现读写操作

转换流

转换流 Transform 其实也是双工流,它与 Duplex 的区别在于,Duplex 虽然同时具备可读流和可写流的功能,但两者是相对独立的,而 Transform 中可读流的数据会经过一定的处理过程自动进入可写流。需要说明的是,从可读流到可写流,它们的数据量不一定相同。例如,常见的压缩、解压缩用的zlib就使用了转换流,压缩和解压缩前后的数据量明显不同。

实现转换流需要进行以下两步。

  1. 继承 Transform 类。

  2. 实现 _transform() 方法。_transform() 方法用来接收数据,并产生输出(需要调用 this.push(data),如果不调用,则接收数据但不输出)。当数据处理完后,必须执行 callback(err, data) 回调函数,该函数中的第一个参数用于传递错误信息,第二个参数用来输出数据(效果和 this.push(data) 相同),但参数可以省略。

使用代码演示上面的两个步骤,其形式如下:

const Stream = require('stream')
class TransformReverse extends stream.Transform {
     constructor() {                               //继承构造函数
          super()
     }
     _transform(data, encoding, callback) {
          this.push(data);
          callback();
     }
}

【例10.5】转换流的使用。(实例位置:资源包\源码\10\05)

创建一个 .js 文件,其中首先通过继承 Transform 自定义一个转换流,在自定义的转换流中重写 _transform() 方法时,实现将写入数据进行反转输出的功能;然后使用转换流的 write() 方法写入数据,并通过监听 data 事件实现数据的读取;最后分别监听 end 事件和 finish 事件,确认读取和写入操作完成。代码如下:

const Stream = require('stream');
class TransformStream extends stream.Transform {
     constructor() {
          super()
     }
     _transform(data, encoding, callback) {
          //将写入的数据进行反转
          const res = data.toString().split('').reverse().join('');
          this.push(res)                                  //输出反转后的数据
          callback()
    }
}
var transformStream = new TransformStream();
transformStream.on('data', data => console.log(data.toString()))
transformStream.on('end', data => console.log('读取完成'));
transformStream.write('写入数据');
transformStream.end()
transformStream.on('finish', data => console.log('写入完成'));

运行结果如图10.7所示。观察图10.7,发现写入的数据在输出时,实现了反转输出。

image 2024 04 14 00 35 56 801
Figure 2. 图10.7 转换流的使用