计算机在搬运数据时,大致流程是:数据源->内存->目的地。当数据很大时,一次性装入内存,对内存的消耗很大,这时就需要把大块数据分割成小块数据传输,这个过程就像水在管道里流动,被称为数据流或者io流。由于数据有来源和目的地,流又分为输入流和输出流。

输入流统称为可读流。比如 stdin就是可读流,它将键盘输入的数据加载到了内存,所以它是可读的。
输出流统称为可写流。比如 stdout就是可写流,它将内存中的数据显示到终端,所以它是可写的。
由此可见,评判输入流还是输出流的依据就是数据是流向/流出内存。

通过管道可以输入流和输出流完成对接

标准输入通过管道连接标准输出

  1. process.stdin.pipe(process.stdout);

Kapture 2020-06-28 at 15.50.50.gif
可以看到输入数据回车后,刚刚输入的内容会输出到终端

服务器响应的可读性流通过管道连接标准输出

const request = require('request');
request('https://baidu.com').pipe(process.stdout);

截屏2020-06-28 下午5.00.26.png

可读流

可读流分3种模式

推送模式(被动模式):
被称为第一代stream, 也就是stream1。

借助 EventEmitter/readable.on(‘data’)/readable.pause()/readable.resume()来实现

拉取模式(主动模式):**
被称为第二代stream, 也就是stream2。
借助 EventEmitter/readable.on(‘readable’)/readable.read()来实现

混合模式(推送和拉取模式一起使用,但是一般不会两种模式同时使用):

var fs = require('fs');
var stream = fs.createReadStream('./assets/1.txt');
stream.setEncoding('utf8');

console.log(`stream.readableFlowing before pause: ${stream.readableFlowing}`)

stream.pause();

var pulledData = '';
var pushedData = '';

console.log(`stream.readableFlowing before readable event binding: ${stream.readableFlowing}`)

stream.on('readable', function() {
  var chunk;
  while(chunk = stream.read()) {
    pulledData += chunk;
  }
});

console.log(`stream.readableFlowing before data event binding: ${stream.readableFlowing}`)

stream.on('data', function(chunk) {
  pushedData += chunk;
});

console.log(`stream.readableFlowing after data event binding: ${stream.readableFlowing}`)

stream.on('end', function() {
  // End of the stream has been reached and no more data can be read
  console.log('Pulled data length: %d', pulledData.length);
  console.log('Pushed data length: %d', pushedData.length);
});

运行结果:

stream.readableFlowing before pause: null
stream.readableFlowing before readable event binding: false
stream.readableFlowing before data event binding: false
stream.readableFlowing after data event binding: false
Pulled data length: 540
Pushed data length: 540

从运行结果看:

事件

close

当流或其底层资源(比如文件描述符)被关闭时触发 'close' 事件。 该事件表明不会再触发其他事件,也不会再发生操作。
如果使用 emitClose 选项创建可写流,则它将会始终发出 'close' 事件。

end

当流中没有数据可供消费时触发。
'end' 事件只有在数据被完全消费掉后才会触发。 要想触发该事件,可以将流转换到流动模式,或反复调用 stream.read()直到数据被消费完。

可写流

事件

close

当流或其底层资源(比如文件描述符)被关闭时触发。 表明不会再触发其他事件,也不会再发生操作。
如果使用 emitClose 选项创建可写流,则它将会始终发出 'close' 事件。

finish

调用 stream.end() 且缓冲数据都已传给底层系统之后触发

finish 先于 close 发生

参考

stream官方文档
stream-handbook
数据生产和消耗的媒介