之前的内容都是在说 如何使用stream,比如说创建一个可读的stream,是通过fs模块创建的。创建一个可写的stream,那是http的response,是http模块提供的,所以目前为止,我们都在使用别人提供的stream。那么现在就是要 创建一个自己的流,给别人用

创建一个Writable Stream

  1. const {Writable} = require("stream");
  2. const outStream = new Writable({
  3. write(chunk, encoding, callback){
  4. console.log(chunk.toString())
  5. callback()
  6. }
  7. })
  8. process.stdin.pipe(outStream);
  9. //保存文件为writable.js 然后用node运行
  10. //不管你输入什么,都会得到相同的结果

process.stdin 是用户的输入stream ,通过管道流向我们自定义的outStream。数据传到了outStream,会把数据打印出来。一定要执行callback(),不然流不会停止。

运行该文件,并输入一些内容
image.png
我们输入什么,就打印什么

所以这个自定义的stream就是输入什么就打印什么,数据是从process.stdin这个进程的标准输入得到的。

创建一个Readable Stream

  1. const { Readable } = require("stream");
  2. const inStream = new Readable();
  3. inStream.push("ABCDEFGHIJKLM");
  4. inStream.push("NOPQRSTUVWXYZ");
  5. inStream.push(null); // No more data
  6. //inStream.pipe(process.stdout); 跟下面是一样的
  7. inStream.on('data',(chunk)=>{
  8. process.stdout.write(chunk)
  9. console.log(`写数据了`)
  10. })
  11. // 保存文件为readable.js 然后 node 运行

当我们在push数据进inSteam的时候,它是处于静止态,因为可读的流默认是静止态,它是不会把数据发出去的。
只有当你监听了它的data事件的时候,就会变成流动态。

运行文件后效果:
image.png
push了两次有效数据,所有写了两次。
现在是把所有数据push进去了,然后监听data事件,这样不太好,要改进一下:

改进 readable.js

改进方案:默认不吐数据,等你问我要的时候,才吐出数据。
关键在于实现一个read方法

  1. const { Readable } = require("stream");
  2. const inStream = new Readable({
  3. read(size) {
  4. const char = String.fromCharCode(this.currentCharCode++)
  5. this.push(char);
  6. console.log(`推了 ${char}`)
  7. if (this.currentCharCode > 90) { // 90 对应的是Z
  8. this.push(null);
  9. }
  10. }
  11. })
  12. inStream.currentCharCode = 65 // 65对应的是A
  13. inStream.pipe(process.stdout)

运行这个文件
image.png

所以,当你要写一个可读的流,最好等别人调用你的read再推,而不是先推好再等别人调。
当然两个方法都没有错,只是改完的这种更好。
因为这次的数据是按需供给的,对方调用read 我们才会给一次数据。

创建一个Duplex Stream

其实只要实现一个可读的流和一个可写的流就可以了
相当于把上面两个的代码复制一遍

  1. const { Duplex } = require("stream");
  2. const inoutStream = new Duplex({
  3. write(chunk, encoding, callback) {
  4. console.log(chunk.toString());
  5. callback();
  6. },
  7. read(size) {
  8. this.push(String.fromCharCode(this.currentCharCode++));
  9. if (this.currentCharCode > 90) {
  10. this.push(null);
  11. }
  12. }
  13. });
  14. inoutStream.currentCharCode = 65;
  15. process.stdin.pipe(inoutStream).pipe(process.stdout);

创建一个Transform Stream

这个既不用实现一个read方法,也不用实现一个write方法,只要实现一个transform方法就可以了。

  1. const { Transform } = require("stream");
  2. const upperCaseTr = new Transform({
  3. transform(chunk, encoding, callback) {
  4. this.push(chunk.toString().toUpperCase());
  5. callback();
  6. }
  7. });
  8. process.stdin.pipe(upperCaseTr).pipe(process.stdout);

关键在于第5行代码: this.push(chunk.toString().toUpperCase());
调用chunk 就是读了,push 就是写了。就相当于 我读你的内容,再变化一下形式,再写给你,就跟babel一样。babel读你的es6,再变成es5,再写给你。

怎么使用:
第10行代码:process.stdin.pipe(upperCaseTr).pipe(process.stdout);
从用户的标准输入里面监听它的data事件,只要用户输入任何一个字,就会调用upperCaseTr这个transform流,这个transform流就会把用户输入的字变成大写的,然后把这些大写的字输出标准输出,用户就会看到自己输入的字变成大写的。

效果:
image.png