Node.js: Stream流

175 阅读3分钟

Stream

案例一

image.png

Stream-流

image.png

释义:

stream是水流,但默认没有水

stream.write可以让水流中有水(数据)

每次写的小数据叫做chunk(块)

产生数据的一段叫做source(源头)

得到数据的一段叫做sink(水池)

案例二

image.png

案例三

image.png

image.png

image.png

Stream对象的原型链

s = fs.createReadStream(path)

  • 那么它的对象层级为

  • 自身属性(由fs.ReadStream构造)

  • 原型:stream.Readable.prototype

  • 二级原型:stream.Stream.prototype

  • 三级原型:events.EventEmitter.prototype

  • 四级原型:Object.prototype

Stream对象都继承了EventEmitter

支持的事件和办法

image.png

drain

//stream1 一有数据就塞给 stream2
stream1.on('data', (chunk)=>{
  const flag = stream2.write(chunk)
  if(flag === false) {// dont write}
  stream2.on('drain', ()=>{
    go on write
  })
})
// stream1停了,就停掉stream2
stream1.on('end', ()=>{
  stream2.end()
})

注意

事件里重点:dataenddrainfinish

方法内的重点:readwrite

参考文档

Stream分类

名称特点
Readable可读
Writable可写
Duplex可读可写(双向)
Transform可读可写(变化)

image.png

Duplex相当于双向的马路,可以一边写一边读,但是,读和写没有交叉点,读和写的内容是不一样的内容,默认读和写的内容是分开的

Transform可以边写边读,可以自己写自己读,中间存在一个转换器(处理器)

Readable Stream

静止态paused和流动态flowing

  • 默认处于paused态

  • 添加data事件监听,它就变为flowing态

  • 删掉data事件监听,它就变为paused态

  • pause()可以将它变为paused

  • resume()可以将它变为flowing

Writable Stream

drain流干了事件

  • 表示可以加点水了

  • 我们调用stream.write(chunk)的时候,可能会得到false

  • false的意思是你写太快了,数据积压了

  • 这个时候我们就不能再write了,要监听drain

  • drain事件触发了,我们才能继续write

function writeOneMillionTimes(writer, data) {
  let i = 1000000;
  write();
  function write() {
    let ok = true;
    do {
      i--;
      if (i === 0) {
        // Last time!
        writer.write(data);
      } else {
        // See if we should continue, or wait.
        // Don't pass the callback, because we're not done yet.
        ok = writer.write(data);
        if (ok === false) {
          console.log("不能再写了");
        }
      }
    } while (i > 0 && ok);
    if (i > 0) {
      // Had to stop early!
      // Write some more once it drains.
      writer.once("drain", () => {
        console.log("干涸了");
        write();
      });
    }
  }
}

const write = fs.createWriteStream("./big_file.txt");
writeOneMillionTimes(write, "hello world");

参考文档

finish事件

  • 调用stream.end()之后,而且缓冲区数据都已经传给底层系统之后,触发finish事件。

创建流

创建一个Writable Stream

const { Writable } = require("stream");

const outStream = new Writable({
  write(chunk, encoding, callback) {
    console.log(chunk.toString());
    callback();
  },
});

process.stdin.pipe(outStream);

image.png

保存文件为writable.js 然后用node运行

不管输入什么,都会得到相同的结果

创建一个Readable Stream

const { Readable } = require("stream");

const inStream = new Readable();
inStream.push("ABCDEFGHIJKLM");
inStream.push("NOPQRSTUVWXYZ");

inStream.push(null);

inStream.pipe(process.stdout);

image.png

保存文件为readable.js 然后用node运行 我们先把所有数据都push进去了,然后pipe

const { Readable } = require("stream");

const inStream = new Readable({
  read(size) {
    this.push(String.fromCharCode(this.currentCharCode++));
    if (this.currentCharCode > 90) {
      this.push(null); //Z
    }
  },
});

inStream.currentCharCode = 65; //A

inStream.pipe(process.stdout);

保存文件为 readable2.js,然后用node运行 这次的数据是按需供给的,对方调用read我们才会给一次数据

Duplex Stream

image.png

Transform Stream

const { Transform } = require("stream");

const upperCaseTr = new Transform({
  transform(chunk, encoding, callback) {
    this.push(chunk.toString().toUpperCase());
    callback();
  },
});
process.stdin.pipe(upperCaseTr).pipe(process.stdout);

image.png

内置的Transform Stream

image.png

image.png

image.png

image.png

Node.js中的Stream

Readable StreamWritable Stream
HTTP Response-客户端HTTP Request—客户端
HTTP Request-服务端HTTP Response-服务端
fs read streamfs write stream
zlib streamzlib stream
TCP socketsTCP socks
child process stdout&stderrchild process stdin
process.stdinprocess.srdin, process,stdin
其他其他

数据流中的积压问题(背压Back Pressure)

参考文档

参考

Node's Streams

Node.js Stream 文档

面试题