概念
Stream 是Node.js中最重要的组件和模式之一,之前在社区中看到这样一句格言“Stream all the things(流是一切)”。
具体的来说流是一组有序的,有起点和终点的字节数据传输手段,它是一个抽象的接口。
流是数据的集合 —— 就像数组或字符串一样。区别在于流中的数据可能不会立刻就全部可用,并且你无需一次性地把这些数据全部放入内存。这使得流在操作大量数据或是数据从外部来源逐段发送过来的时候变得非常有用。
每一个流对象都是EventEmitter类的一个实例,都有相应的on和emit方法,在下文中演示的代码示例中我们会看到。
stream 是node的核心模块,引入方式如下:
let Stream = require('stream')
实现形式
流在node中有多种实现形式,例如:
1. http协议中的 请求req和响应res
2. tcp协议中的套接字对象sockets
3. fs文件模块中的可读流和可写流
4. process进程模块中的stdout stderr
5. zlib 中的streams .....
类型
提供了以下四种类型:
可读流(Readable)
let Readable = stream.Readable
可写流(Writeable)
let Readable = stream.Writeable
可读写流(Duplex)
let Duplex = stream.Duplex
转换流(Transform)
let Transform = stream.Transform
原理
Readable
实现了stream.Readable接口的对象,将对象数据读取为流数据,当监听data事件后,开始发射数据.
1. 创建
let rs = fs.createReadStream(path,{flags: 'r', // 打开文件要做的操作,默认为'r'encoding: null, // 默认为nullstart: '0', // 开始读取的索引位置end: '', // 结束读取的索引位置(包括结束位置)highWaterMark: '', // 读取缓存区默认的大小的阀值64kb
})
2. 方法及作用
// 1.监听data事件 流自动切换到流动模式
// 2.数据会尽可能快的读出
rs.on('data', function (data) {console.log(data);
});
// 数据读取完毕后触发end事件
rs.on('end', function () {console.log('读取完成');
});
// 可读流打开事件
rs.on('open', function () {console.log(err);
});
// 可读流关闭事件
rs.on('close', function () {console.log(err);
});
// 指定编码 和上面创建流时的参数encoding意思相同
rs.setEncoding('utf8');
rs.on('data', function (data) {
// 可读流暂停读取rs.pause();console.log(data);
});
setTimeout(function () {
// 可读流恢复读取rs.resume();
},2000);
3. 分类
可读流分为:流动模式和暂停模式
可读流对象readable中有一个维护状态的对象,readable._readableState,这里简称为state。 其中有一个标记,state.flowing, 可用来判别流的模式。 它有三种可能值:
true 流动模式。 false 暂停模式。 null 初始状态。
1) 流动模式(flowing mode)
流动模式下,数据会源源不断地生产出来,形成“流动”现象。 监听流的data事件便可进入该模式。
2) 暂停模式(paused mode)
暂停模式下,需要显示地调用read(),触发data事件。
在初始状态下,监听data事件,会使流进入流动模式。 但如果在暂停模式下,监听data事件并不会使它进入流动模式。 为了消耗流,需要显示调用read()方法。
3)相互转化
调用readable.resume()可使流进入流动模式,state.flowing被设为true。 调用readable.pause()可使流进入暂停模式,state.flowing被设为false。
4. 原理
创建可读流时,需要继承Readable,并实现_read方法。
1._read方法是从底层系统读取具体数据的逻辑,即生产数据的逻辑。在_read方法中,通过调用push(data)将数据放入可读流中供下游消耗。 在_read方法中,可以同步调用push(data),也可以异步调用。 当全部数据都生产出来后,必须调用push(null)来结束可读流。 流一旦结束,便不能再调用push(data)添加数据。 可以通过监听data事件的方式消耗可读流。在首次监听其data事件后,readable便会持续不断地调用_read(),通过触发data事件将数据输出。 第一次data事件会在下一个tick中触发,所以,可以安全地将数据输出前的逻辑放在事件监听后(同一个tick中)。 当数据全部被消耗时,会触发end事件。2.详解