可读流 - nodejs stream总结
可读流
包含的事件:data,readable,end,close ,error,pause,resume
常用方法:resume,read,pipe,pause
客户端的 HTTP 响应
服务器的 HTTP 请求
fs 的读取流
zlib 流
crypto 流
TCP socket
子进程 stdout 与 stderr
process.stdin
实现可读流:
const {Readable} = require('stream');
let i = 0;
const rs = Readable({
  encoding: 'utf8',
  highWaterMark: 9,
  // 这里传入的read方法,会被写入_read()
  read: (size) => {
    // size 为highWaterMark大小
    // 在这个方法里面实现获取数据,读取到数据调用rs.push([data]),如果没有数据了,push(null)结束流
    if (i < 10) {
      // push其实是把数据放入缓存区
      console.log(1);
      rs.push(`当前读取数据: ${i++}`);
    } else {
      rs.push(null);
    }
  }
});
rs.on('readable', () => {
  const data = rs.read(9)
  console.log(data);
})
// 结果: 当前读取数据: 0 .....   当前读取数据: 9
readable事件
触发时机 :当有数据可从流中读取时,就会触发readable 事件。如果流有新的动态,将会触发readable,比如push了新的内容;所以下面的代码将会触发两次readable;第二次之后缓冲区的内容多于highWaterMark,不再执行read方法,结果流没有变化,进而readable不再触发。
const {Readable} = require('stream');
let i = 0;
const rs = Readable({
  encoding: 'utf8',
  highWaterMark: 9,
  read: (size) => {
    if (i < 2) {
      rs.push(`当前读取数据: ${i++}`);
    } else {
      rs.push(null);
    }
  }
});
//  在某些情况下,为 'readable' 事件附加监听器将会导致将一些数据读入内部缓冲区,也就是会调用read方法。
rs.on('readable', () => {
  console.log(rs.readableFlowing);  // false 进入暂停模式
  const data = rs.read(1)
  console.log(data);
})
// 结果 : 当 前
当到达流数据的尽头时, readable事件也会触发,但是在end事件之前触发,上面代码修改一下:
rs.on('readable', () => {
  console.log('readable');
  let data = ''
  while (data=rs.read(1)){
    console.log(rs.readableLength);
  }
  console.log(data);
})
// 在缓冲区内没有数据之后rs.readableLength为0,结尾有两个readable打印出来,最后一个代表流数据到达了尽头触发了readable
readable事件表明流有新的动态:要么有新的数据,要么到达流的尽头。 对于前者,stream.read() 会返回可用的数据。 对于后者,stream.read() 会返回 null。
添加 readable事件句柄会使流自动停止流动,并通过 readable.read() 消费数据(调用一次内部read方法)。 如果 readable事件句柄被移除,且存在 data 事件句柄,无需resume,流会再次开始流动。
data 事件
当流将数据块传送给消费者后触发。 当调用
readable.pipe(),readable.resume()或绑定监听器到data事件时,流会转换到流动模式。 当调用readable.read()且有数据块返回时,也会触发data事件。将
data事件监听器附加到尚未显式暂停的流将会使流切换为流动模式。 数据将会在可用时立即传递。如果使用
readable.setEncoding()为流指定了默认的字符编码,则监听器回调传入的数据为字符串,否则传入的数据为Buffer。
const {Readable} = require('stream');
let i = 0;
const rs = Readable({
  encoding: 'utf8',
  // 这里传入的read方法,会被写入_read()
  read: (size) => {
    // size 为highWaterMark大小
    // 在这个方法里面实现获取数据,读取到数据调用rs.push([data]),如果没有数据了,push(null)结束流
    if (i < 6) {
      rs.push(`当前读取数据: ${i++}`);
    } else {
      rs.push(null);
    }
  },
  destroy(err, cb) {
    rs.push(null);
    cb(err);
  }
});
rs.on('data', (data) => {
  console.log(data);
  console.log(rs.readableFlowing);  // true  进入流动模式
})
如果同时使用 readable事件和data事件,则 readable事件会优先控制流,readableFlowing为false;当调用 stream.read() 时才会触发 data事件
const {Readable} = require('stream');
let i = 0;
const rs = Readable({
  encoding: 'utf8',
  highWaterMark: 9,
  read: (size) => {
    if (i < 10) {
      rs.push(`当前读取数据: ${i++}`);
    } else {
      rs.push(null);
    }
  }
});
rs.on('readable', () => {
  const data = rs.read()
  console.log(data);
  console.log(rs.readableFlowing); // false
})
rs.on('data', (data) => {
  console.log(rs.readableFlowing); // false
  console.log(data);
})
// 即便用了data事件,因为由readable事件,可读流一直处于暂停模式
移除readable重新触发data事件
const {Duplex} = require('stream');
class Duplexer extends Duplex {
  constructor(props) {
    super(props);
    this.data = [];
  }
  _read(size) {
    const chunk = this.data.shift();
    if (chunk == 'stop') {
      this.push(null);
    } else {
      if (chunk) {
        this.push(chunk);
      }
    }
  }
  _write(chunk, encoding, cb) {
    this.data.push(chunk);
    cb();
  }
}
const d = new Duplexer({allowHalfOpen: true});
d.write('...大沙发撒地方是.');
d.write('阿斯顿发斯蒂芬');
d.write('阿斯顿发斯蒂芬11');
d.write('stop');
d.end()
var a = function (a) {}
d.on('readable', a);
d.on('data', function (data) {
  console.log(data.toString());
});
d.removeListener('readable', a)
end 事件
只有在数据被完全消费掉后才会触发; 要想触发该事件,可以将流转换到流动模式,或反复调用 stream.read() 直到数据被消费完。
使用 readable.read() 处理数据时, while 循环是必需的。
const {Readable} = require('stream');
let i = 0;
const rs = Readable({
  encoding: 'utf8',
  highWaterMark: 9,
  read: (size) => {
    if (i < 2) {
      rs.push(`当前读取数据: ${i++}`);
    } else {
      rs.push(null);
    }
  }
});
//  在某些情况下,为 'readable' 事件附加监听器将会导致将一些数据读入内部缓冲区,也就是会调用read方法。
rs.on('readable', () => {
  while (data=rs.read(1)){
    console.log(rs.readableLength);
  }
})
rs.on('end', () => {
  console.log('end');
})
highWaterMark
执行read方法的阈值;如果缓冲区内容长度大于highWaterMark,read方法将不会执行。
const {Readable} = require('stream');
let i = 0;
const rs = Readable({
  encoding: 'utf8',
  highWaterMark: 9,
  read: (size) => {
    if (i < 10) {
      rs.push(`当前读取数据: ${i++}`);
    } else {
      rs.push(null);
    }
  }
});
rs.on('readable', () => {
  console.log(rs.readableLength);
  const data = rs.read(1)
  console.log(data);
})
// 打印结果: 9 当 17 前
解析:第二次rs.read(1)时候,缓冲区的内容长度为16,大于highWaterMark,导致不能触发内部read方法。
resume pause close事件
这个例子使用了双工流
const {Duplex} = require('stream');
class Duplexer extends Duplex {
  constructor(props) {
    super(props);
    this.data = [];
  }
  _read(size) {
    const chunk = this.data.shift();
    if (chunk == 'stop') {
      this.push(null);
    } else {
      if (chunk) {
        this.push(chunk);
      }
    }
  }
  _write(chunk, encoding, cb) {
    this.data.push(chunk);
    cb();
  }
}
const d = new Duplexer({allowHalfOpen: true});
d.write('第一行');
d.write('第二行');
d.write('第三行');
d.write('stop');
d.end()
d.on('data', function (chunk) {
  console.log('read: ', chunk.toString());
  d.pause()
  setTimeout(() => {
    d.resume()
  }, 2000)
});
d.on('pause',function () {
  console.log('pause');
})
d.on('resume',function () {
  console.log('resume');
})
d.on('close',function () {
  console.log('close');
})
模式
可读流中分为2种模式流动模式和暂停模式。
1、流动模式:可读流自动读取数据,通过EventEmitter接口的事件尽快将数据提供给应用。
2、暂停模式:必须显式调用stream.read()方法来从流中读取数据片段。
暂停模式切换到流动模式i:
1、监听“data”事件
2、调用 stream.resume()方法
3、调用 stream.pipe()方法将数据发送到可写流
流动模式切换到暂停模式:
1、如果不存在管道目标,调用stream.pause()方法
2、如果存在管道目标,调用 stream.unpipe()并取消'data'事件监听
可读流 - nodejs stream总结的更多相关文章
- NodeJS Stream 五:双工流
		双工流就是同时实现了 Readable 和 Writable 的流,即可以作为上游生产数据,又可以作为下游消费数据,这样可以处于数据流动管道的中间部分,即 rs.pipe(rws1).pipe(rws ... 
- nodejs笔记之流(stream)
		nodejs的stream有四种流类型: 可读:Readable可写:Writable可读可写:Duplex操作被写入数据,然后读出结果:Transform常用事件:data:有数据可读时触发end: ... 
- NodeJS Stream流
		NodeJS Stream流 流数据在网络通信中至关重要,nodeJS用Stream提供了一个抽象接口,node中有很多对象实现了这个接口,提供统一的操作体验 基本流类型 NodeJS中,Stream ... 
- nodeJs文件系统(fs)与流(stream)
		一.简介 本文将介绍node.js文件系统(fs)和流(stream)的一些API已经参数使用情况. 二.目录 文件系统将介绍以下方法: 1.fs.readFile 2.fs.writeFile 3. ... 
- node.js中stream流中可读流和可写流的使用
		node.js中的流 stream 是处理流式数据的抽象接口.node.js 提供了很多流对象,像http中的request和response,和 process.stdout 都是流的实例. 流可以 ... 
- NodeJS Stream 三:readable
		什么是可读流 可读流是生产数据用来供程序消费的流.我们常见的数据生产方式有读取磁盘文件.读取网络请求内容等,看一下前面介绍什么是流用的例子: const rs = fs.createReadStrea ... 
- nodejs stream 手册学习
		nodejs stream 手册 https://github.com/jabez128/stream-handbook 在node中,流可以帮助我们将事情的重点分为几份,因为使用流可以帮助我们将实现 ... 
- Nodejs stream模块-翻译
		花了两天时间尝试按照自己的话翻译了一下stream模块,以下内容皆翻译于:https://nodejs.org/api/stream.html. 目录 1 Stream(流) 1.1 ... 
- nodejs stream基础知识
		分类 nodejs 的 stream 有四种: Readable:可读流 Writable: 可写流 Duplex:双工流 Transform:转换流 Readable // _read方法是从底层系 ... 
随机推荐
- shell专题(三):Shell脚本入门
			1.脚本格式 脚本以#!/bin/bash开头(指定解析器) 2.第一个Shell脚本:helloworld (1)需求:创建一个Shell脚本,输出helloworld (2)案例实操: [atgu ... 
- vue 集成html5 plus
			首先要安装一个包 vue-html5plus npm i vue-html5plus -S 然后配置这个文件 在main.js添加一串代码 var onPlusReady = function (ca ... 
- 定时器之Timer
			Timer中的TimerTask就是一个线程,可以一直执行下去的.可以使用Timer类的cancel方法来结束.-------------------------------------------- ... 
- JVM详解之:运行时常量池
			目录 简介 class文件中的常量池 运行时常量池 静态常量详解 String常量 数字常量 符号引用详解 String Pool字符串常量池 总结 简介 JVM在运行的时候会对class文件进行加载 ... 
- Ethical Hacking - NETWORK PENETRATION TESTING(4)
			Targeted packet sniffing airodump-ng --channel[channel] --bssid[bssid] --write[file-name][interface] ... 
- .Net、ASP.Net、C#、VisualStudio之间的关系是什么
			.Net一般指的是.NetFramework,提供了基础的.Net类,这些类可以被任何一种.Net编程语言调 用,.NetFramework还提供了 CLR.JIT.GC等基础功能. ASP.Net是 ... 
- python mysql中in参数化说明
			第一种:拼接字符串,可以解决问题,但是为了避免sql注入,不建议这样写 还是看看第二种:使用.format()函数,很多时候我都是使用这个函数来对sql参数化的 举个例子: select * from ... 
- js原型链结构理解
			在一般的面向对象的语言中,都存在类(class)的概念,类就是对象的模板,对象就是类的实例. 但在js中是没有类的定义的(万物皆是对象). 题外话:但是在ES6中提供了更接近传统语言的写法,引入了C ... 
- 带你理解Lock锁原理
			同样是锁,先说说synchronized和lock的区别: synchronized是java关键字,是用c++实现的:而lock是用java类,用java可以实现 synchronized可以锁住代 ... 
- ~~网络编程(四):socket套接字~~
			进击のpython ***** 网络编程--socket socket的中文意思叫做套接字,socket方法其实也叫套接字方法 我们研究过TCP/UDP协议,但是要是让我们自己搭建,就十分困难了 而这 ... 
